[SCSI] be2iscsi: add 10Gbps iSCSI - BladeEngine 2 driver

[v2: fixed up virt_to_bus() issue spotted by sfr]
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: Jayamohan Kallickal <jayamohank@serverengines.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
Jayamohan Kallickal 2009-09-05 07:36:35 +05:30 committed by James Bottomley
parent d74cf7c3e9
commit 6733b39a13
14 changed files with 7126 additions and 0 deletions

View File

@ -4625,6 +4625,14 @@ F: drivers/ata/
F: include/linux/ata.h
F: include/linux/libata.h
SERVER ENGINES 10Gbps iSCSI - BladeEngine 2 DRIVER
P: Jayamohan Kallickal
M: jayamohank@serverengines.com
L: linux-scsi@vger.kernel.org
W: http://www.serverengines.com
S: Supported
F: drivers/scsi/be2iscsi/
SERVER ENGINES 10Gbps NIC - BladeEngine 2 DRIVER
M: Sathya Perla <sathyap@serverengines.com>
M: Subbu Seetharaman <subbus@serverengines.com>

View File

@ -366,6 +366,7 @@ config ISCSI_TCP
source "drivers/scsi/cxgb3i/Kconfig"
source "drivers/scsi/bnx2i/Kconfig"
source "drivers/scsi/be2iscsi/Kconfig"
config SGIWD93_SCSI
tristate "SGI WD93C93 SCSI Driver"

View File

@ -131,6 +131,7 @@ obj-$(CONFIG_SCSI_MVSAS) += mvsas/
obj-$(CONFIG_PS3_ROM) += ps3rom.o
obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/
obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/
obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o
obj-$(CONFIG_ARM) += arm/

View File

@ -0,0 +1,8 @@
config BE2ISCSI
tristate "ServerEngines' 10Gbps iSCSI - BladeEngine 2"
depends on PCI && SCSI
select SCSI_ISCSI_ATTRS
help
This driver implements the iSCSI functionality for ServerEngines'
10Gbps Storage adapter - BladeEngine 2.

View File

@ -0,0 +1,8 @@
#
# Makefile to build the iSCSI driver for ServerEngine's BladeEngine.
#
#
obj-$(CONFIG_BE2ISCSI) += be2iscsi.o
be2iscsi-y := be_iscsi.o be_main.o be_mgmt.o be_cmds.o

183
drivers/scsi/be2iscsi/be.h Normal file
View File

@ -0,0 +1,183 @@
/**
* Copyright (C) 2005 - 2009 ServerEngines
* 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.
*
* Contact Information:
* linux-drivers@serverengines.com
*
* ServerEngines
* 209 N. Fair Oaks Ave
* Sunnyvale, CA 94085
*/
#ifndef BEISCSI_H
#define BEISCSI_H
#include <linux/pci.h>
#include <linux/if_vlan.h>
#define FW_VER_LEN 32
struct be_dma_mem {
void *va;
dma_addr_t dma;
u32 size;
};
struct be_queue_info {
struct be_dma_mem dma_mem;
u16 len;
u16 entry_size; /* Size of an element in the queue */
u16 id;
u16 tail, head;
bool created;
atomic_t used; /* Number of valid elements in the queue */
};
static inline u32 MODULO(u16 val, u16 limit)
{
WARN_ON(limit & (limit - 1));
return val & (limit - 1);
}
static inline void index_inc(u16 *index, u16 limit)
{
*index = MODULO((*index + 1), limit);
}
static inline void *queue_head_node(struct be_queue_info *q)
{
return q->dma_mem.va + q->head * q->entry_size;
}
static inline void *queue_tail_node(struct be_queue_info *q)
{
return q->dma_mem.va + q->tail * q->entry_size;
}
static inline void queue_head_inc(struct be_queue_info *q)
{
index_inc(&q->head, q->len);
}
static inline void queue_tail_inc(struct be_queue_info *q)
{
index_inc(&q->tail, q->len);
}
/*ISCSI */
struct be_eq_obj {
struct be_queue_info q;
char desc[32];
/* Adaptive interrupt coalescing (AIC) info */
bool enable_aic;
u16 min_eqd; /* in usecs */
u16 max_eqd; /* in usecs */
u16 cur_eqd; /* in usecs */
};
struct be_mcc_obj {
struct be_queue_info *q;
struct be_queue_info *cq;
};
struct be_ctrl_info {
u8 __iomem *csr;
u8 __iomem *db; /* Door Bell */
u8 __iomem *pcicfg; /* PCI config space */
struct pci_dev *pdev;
/* Mbox used for cmd request/response */
spinlock_t mbox_lock; /* For serializing mbox cmds to BE card */
struct be_dma_mem mbox_mem;
/* Mbox mem is adjusted to align to 16 bytes. The allocated addr
* is stored for freeing purpose */
struct be_dma_mem mbox_mem_alloced;
/* MCC Rings */
struct be_mcc_obj mcc_obj;
spinlock_t mcc_lock; /* For serializing mcc cmds to BE card */
spinlock_t mcc_cq_lock;
/* MCC Async callback */
void (*async_cb) (void *adapter, bool link_up);
void *adapter_ctxt;
};
#include "be_cmds.h"
#define PAGE_SHIFT_4K 12
#define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K)
/* Returns number of pages spanned by the data starting at the given addr */
#define PAGES_4K_SPANNED(_address, size) \
((u32)((((size_t)(_address) & (PAGE_SIZE_4K - 1)) + \
(size) + (PAGE_SIZE_4K - 1)) >> PAGE_SHIFT_4K))
/* Byte offset into the page corresponding to given address */
#define OFFSET_IN_PAGE(addr) \
((size_t)(addr) & (PAGE_SIZE_4K-1))
/* Returns bit offset within a DWORD of a bitfield */
#define AMAP_BIT_OFFSET(_struct, field) \
(((size_t)&(((_struct *)0)->field))%32)
/* Returns the bit mask of the field that is NOT shifted into location. */
static inline u32 amap_mask(u32 bitsize)
{
return (bitsize == 32 ? 0xFFFFFFFF : (1 << bitsize) - 1);
}
static inline void amap_set(void *ptr, u32 dw_offset, u32 mask,
u32 offset, u32 value)
{
u32 *dw = (u32 *) ptr + dw_offset;
*dw &= ~(mask << offset);
*dw |= (mask & value) << offset;
}
#define AMAP_SET_BITS(_struct, field, ptr, val) \
amap_set(ptr, \
offsetof(_struct, field)/32, \
amap_mask(sizeof(((_struct *)0)->field)), \
AMAP_BIT_OFFSET(_struct, field), \
val)
static inline u32 amap_get(void *ptr, u32 dw_offset, u32 mask, u32 offset)
{
u32 *dw = ptr;
return mask & (*(dw + dw_offset) >> offset);
}
#define AMAP_GET_BITS(_struct, field, ptr) \
amap_get(ptr, \
offsetof(_struct, field)/32, \
amap_mask(sizeof(((_struct *)0)->field)), \
AMAP_BIT_OFFSET(_struct, field))
#define be_dws_cpu_to_le(wrb, len) swap_dws(wrb, len)
#define be_dws_le_to_cpu(wrb, len) swap_dws(wrb, len)
static inline void swap_dws(void *wrb, int len)
{
#ifdef __BIG_ENDIAN
u32 *dw = wrb;
WARN_ON(len % 4);
do {
*dw = cpu_to_le32(*dw);
dw++;
len -= 4;
} while (len);
#endif /* __BIG_ENDIAN */
}
extern void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
u16 num_popped);
#endif /* BEISCSI_H */

View File

@ -0,0 +1,523 @@
/**
* Copyright (C) 2005 - 2009 ServerEngines
* 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.
*
* Contact Information:
* linux-drivers@serverengines.com
*
* ServerEngines
* 209 N. Fair Oaks Ave
* Sunnyvale, CA 94085
*/
#include "be.h"
#include "be_mgmt.h"
#include "be_main.h"
static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl)
{
if (compl->flags != 0) {
compl->flags = le32_to_cpu(compl->flags);
WARN_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0);
return true;
} else
return false;
}
static inline void be_mcc_compl_use(struct be_mcc_compl *compl)
{
compl->flags = 0;
}
static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
struct be_mcc_compl *compl)
{
u16 compl_status, extd_status;
be_dws_le_to_cpu(compl, 4);
compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
CQE_STATUS_COMPL_MASK;
if (compl_status != MCC_STATUS_SUCCESS) {
extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
CQE_STATUS_EXTD_MASK;
dev_err(&ctrl->pdev->dev,
"error in cmd completion: status(compl/extd)=%d/%d\n",
compl_status, extd_status);
return -1;
}
return 0;
}
static inline bool is_link_state_evt(u32 trailer)
{
return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
ASYNC_TRAILER_EVENT_CODE_MASK) == ASYNC_EVENT_CODE_LINK_STATE);
}
void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
u16 num_popped)
{
u32 val = 0;
val |= qid & DB_CQ_RING_ID_MASK;
if (arm)
val |= 1 << DB_CQ_REARM_SHIFT;
val |= num_popped << DB_CQ_NUM_POPPED_SHIFT;
iowrite32(val, ctrl->db + DB_CQ_OFFSET);
}
static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl)
{
#define long_delay 2000
void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET;
int cnt = 0, wait = 5; /* in usecs */
u32 ready;
do {
ready = ioread32(db) & MPU_MAILBOX_DB_RDY_MASK;
if (ready)
break;
if (cnt > 6000000) {
dev_err(&ctrl->pdev->dev, "mbox_db poll timed out\n");
return -1;
}
if (cnt > 50) {
wait = long_delay;
mdelay(long_delay / 1000);
} else
udelay(wait);
cnt += wait;
} while (true);
return 0;
}
int be_mbox_notify(struct be_ctrl_info *ctrl)
{
int status;
u32 val = 0;
void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET;
struct be_dma_mem *mbox_mem = &ctrl->mbox_mem;
struct be_mcc_mailbox *mbox = mbox_mem->va;
struct be_mcc_compl *compl = &mbox->compl;
val &= ~MPU_MAILBOX_DB_RDY_MASK;
val |= MPU_MAILBOX_DB_HI_MASK;
val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2;
iowrite32(val, db);
status = be_mbox_db_ready_wait(ctrl);
if (status != 0) {
SE_DEBUG(DBG_LVL_1, " be_mbox_db_ready_wait failed 1\n");
return status;
}
val = 0;
val &= ~MPU_MAILBOX_DB_RDY_MASK;
val &= ~MPU_MAILBOX_DB_HI_MASK;
val |= (u32) (mbox_mem->dma >> 4) << 2;
iowrite32(val, db);
status = be_mbox_db_ready_wait(ctrl);
if (status != 0) {
SE_DEBUG(DBG_LVL_1, " be_mbox_db_ready_wait failed 2\n");
return status;
}
if (be_mcc_compl_is_new(compl)) {
status = be_mcc_compl_process(ctrl, &mbox->compl);
be_mcc_compl_use(compl);
if (status) {
SE_DEBUG(DBG_LVL_1, "After be_mcc_compl_process \n");
return status;
}
} else {
dev_err(&ctrl->pdev->dev, "invalid mailbox completion\n");
return -1;
}
return 0;
}
void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
bool embedded, u8 sge_cnt)
{
if (embedded)
wrb->embedded |= MCC_WRB_EMBEDDED_MASK;
else
wrb->embedded |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) <<
MCC_WRB_SGE_CNT_SHIFT;
wrb->payload_length = payload_len;
be_dws_cpu_to_le(wrb, 8);
}
void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
u8 subsystem, u8 opcode, int cmd_len)
{
req_hdr->opcode = opcode;
req_hdr->subsystem = subsystem;
req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr));
}
static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
struct be_dma_mem *mem)
{
int i, buf_pages;
u64 dma = (u64) mem->dma;
buf_pages = min(PAGES_4K_SPANNED(mem->va, mem->size), max_pages);
for (i = 0; i < buf_pages; i++) {
pages[i].lo = cpu_to_le32(dma & 0xFFFFFFFF);
pages[i].hi = cpu_to_le32(upper_32_bits(dma));
dma += PAGE_SIZE_4K;
}
}
static u32 eq_delay_to_mult(u32 usec_delay)
{
#define MAX_INTR_RATE 651042
const u32 round = 10;
u32 multiplier;
if (usec_delay == 0)
multiplier = 0;
else {
u32 interrupt_rate = 1000000 / usec_delay;
if (interrupt_rate == 0)
multiplier = 1023;
else {
multiplier = (MAX_INTR_RATE - interrupt_rate) * round;
multiplier /= interrupt_rate;
multiplier = (multiplier + round / 2) / round;
multiplier = min(multiplier, (u32) 1023);
}
}
return multiplier;
}
struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem)
{
return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb;
}
int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *eq, int eq_delay)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_cmd_req_eq_create *req = embedded_payload(wrb);
struct be_cmd_resp_eq_create *resp = embedded_payload(wrb);
struct be_dma_mem *q_mem = &eq->dma_mem;
int status;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_EQ_CREATE, sizeof(*req));
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
AMAP_SET_BITS(struct amap_eq_context, func, req->context,
PCI_FUNC(ctrl->pdev->devfn));
AMAP_SET_BITS(struct amap_eq_context, valid, req->context, 1);
AMAP_SET_BITS(struct amap_eq_context, size, req->context, 0);
AMAP_SET_BITS(struct amap_eq_context, count, req->context,
__ilog2_u32(eq->len / 256));
AMAP_SET_BITS(struct amap_eq_context, delaymult, req->context,
eq_delay_to_mult(eq_delay));
be_dws_cpu_to_le(req->context, sizeof(req->context));
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
status = be_mbox_notify(ctrl);
if (!status) {
eq->id = le16_to_cpu(resp->eq_id);
eq->created = true;
}
spin_unlock(&ctrl->mbox_lock);
return status;
}
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
int status;
u8 *endian_check;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
endian_check = (u8 *) wrb;
*endian_check++ = 0xFF;
*endian_check++ = 0x12;
*endian_check++ = 0x34;
*endian_check++ = 0xFF;
*endian_check++ = 0xFF;
*endian_check++ = 0x56;
*endian_check++ = 0x78;
*endian_check++ = 0xFF;
be_dws_cpu_to_le(wrb, sizeof(*wrb));
status = be_mbox_notify(ctrl);
if (status)
SE_DEBUG(DBG_LVL_1, "be_cmd_fw_initialize Failed \n");
spin_unlock(&ctrl->mbox_lock);
return status;
}
int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *cq, struct be_queue_info *eq,
bool sol_evts, bool no_delay, int coalesce_wm)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_cmd_req_cq_create *req = embedded_payload(wrb);
struct be_cmd_resp_cq_create *resp = embedded_payload(wrb);
struct be_dma_mem *q_mem = &cq->dma_mem;
void *ctxt = &req->context;
int status;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_CQ_CREATE, sizeof(*req));
if (!q_mem->va)
SE_DEBUG(DBG_LVL_1, "uninitialized q_mem->va\n");
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
AMAP_SET_BITS(struct amap_cq_context, coalescwm, ctxt, coalesce_wm);
AMAP_SET_BITS(struct amap_cq_context, nodelay, ctxt, no_delay);
AMAP_SET_BITS(struct amap_cq_context, count, ctxt,
__ilog2_u32(cq->len / 256));
AMAP_SET_BITS(struct amap_cq_context, valid, ctxt, 1);
AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts);
AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1);
AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id);
AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1);
AMAP_SET_BITS(struct amap_cq_context, func, ctxt,
PCI_FUNC(ctrl->pdev->devfn));
be_dws_cpu_to_le(ctxt, sizeof(req->context));
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
status = be_mbox_notify(ctrl);
if (!status) {
cq->id = le16_to_cpu(resp->cq_id);
cq->created = true;
} else
SE_DEBUG(DBG_LVL_1, "In be_cmd_cq_create, status=ox%08x \n",
status);
spin_unlock(&ctrl->mbox_lock);
return status;
}
static u32 be_encoded_q_len(int q_len)
{
u32 len_encoded = fls(q_len); /* log2(len) + 1 */
if (len_encoded == 16)
len_encoded = 0;
return len_encoded;
}
int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
int queue_type)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_cmd_req_q_destroy *req = embedded_payload(wrb);
u8 subsys = 0, opcode = 0;
int status;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
switch (queue_type) {
case QTYPE_EQ:
subsys = CMD_SUBSYSTEM_COMMON;
opcode = OPCODE_COMMON_EQ_DESTROY;
break;
case QTYPE_CQ:
subsys = CMD_SUBSYSTEM_COMMON;
opcode = OPCODE_COMMON_CQ_DESTROY;
break;
case QTYPE_WRBQ:
subsys = CMD_SUBSYSTEM_ISCSI;
opcode = OPCODE_COMMON_ISCSI_WRBQ_DESTROY;
break;
case QTYPE_DPDUQ:
subsys = CMD_SUBSYSTEM_ISCSI;
opcode = OPCODE_COMMON_ISCSI_DEFQ_DESTROY;
break;
case QTYPE_SGL:
subsys = CMD_SUBSYSTEM_ISCSI;
opcode = OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES;
break;
default:
spin_unlock(&ctrl->mbox_lock);
BUG();
return -1;
}
be_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req));
if (queue_type != QTYPE_SGL)
req->id = cpu_to_le16(q->id);
status = be_mbox_notify(ctrl);
spin_unlock(&ctrl->mbox_lock);
return status;
}
int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb);
int status;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
sizeof(*req));
status = be_mbox_notify(ctrl);
if (!status) {
struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb);
memcpy(mac_addr, resp->mac_address, ETH_ALEN);
}
spin_unlock(&ctrl->mbox_lock);
return status;
}
int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
struct be_queue_info *cq,
struct be_queue_info *dq, int length,
int entry_size)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_defq_create_req *req = embedded_payload(wrb);
struct be_dma_mem *q_mem = &dq->dma_mem;
void *ctxt = &req->context;
int status;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
OPCODE_COMMON_ISCSI_DEFQ_CREATE, sizeof(*req));
req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
AMAP_SET_BITS(struct amap_be_default_pdu_context, rx_pdid, ctxt, 0);
AMAP_SET_BITS(struct amap_be_default_pdu_context, rx_pdid_valid, ctxt,
1);
AMAP_SET_BITS(struct amap_be_default_pdu_context, pci_func_id, ctxt,
PCI_FUNC(ctrl->pdev->devfn));
AMAP_SET_BITS(struct amap_be_default_pdu_context, ring_size, ctxt,
be_encoded_q_len(length / sizeof(struct phys_addr)));
AMAP_SET_BITS(struct amap_be_default_pdu_context, default_buffer_size,
ctxt, entry_size);
AMAP_SET_BITS(struct amap_be_default_pdu_context, cq_id_recv, ctxt,
cq->id);
be_dws_cpu_to_le(ctxt, sizeof(req->context));
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
status = be_mbox_notify(ctrl);
if (!status) {
struct be_defq_create_resp *resp = embedded_payload(wrb);
dq->id = le16_to_cpu(resp->id);
dq->created = true;
}
spin_unlock(&ctrl->mbox_lock);
return status;
}
int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
struct be_queue_info *wrbq)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_wrbq_create_req *req = embedded_payload(wrb);
struct be_wrbq_create_resp *resp = embedded_payload(wrb);
int status;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
OPCODE_COMMON_ISCSI_WRBQ_CREATE, sizeof(*req));
req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
status = be_mbox_notify(ctrl);
if (!status)
wrbq->id = le16_to_cpu(resp->cid);
spin_unlock(&ctrl->mbox_lock);
return status;
}
int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl,
struct be_dma_mem *q_mem,
u32 page_offset, u32 num_pages)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_post_sgl_pages_req *req = embedded_payload(wrb);
int status;
unsigned int curr_pages;
u32 internal_page_offset = 0;
u32 temp_num_pages = num_pages;
if (num_pages == 0xff)
num_pages = 1;
spin_lock(&ctrl->mbox_lock);
do {
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
OPCODE_COMMON_ISCSI_CFG_POST_SGL_PAGES,
sizeof(*req));
curr_pages = BE_NUMBER_OF_FIELD(struct be_post_sgl_pages_req,
pages);
req->num_pages = min(num_pages, curr_pages);
req->page_offset = page_offset;
be_cmd_page_addrs_prepare(req->pages, req->num_pages, q_mem);
q_mem->dma = q_mem->dma + (req->num_pages * PAGE_SIZE);
internal_page_offset += req->num_pages;
page_offset += req->num_pages;
num_pages -= req->num_pages;
if (temp_num_pages == 0xff)
req->num_pages = temp_num_pages;
status = be_mbox_notify(ctrl);
if (status) {
SE_DEBUG(DBG_LVL_1,
"FW CMD to map iscsi frags failed.\n");
goto error;
}
} while (num_pages > 0);
error:
spin_unlock(&ctrl->mbox_lock);
if (status != 0)
beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
return status;
}

View File

@ -0,0 +1,877 @@
/**
* Copyright (C) 2005 - 2009 ServerEngines
* 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.
*
* Contact Information:
* linux-drivers@serverengines.com
*
* ServerEngines
* 209 N. Fair Oaks Ave
* Sunnyvale, CA 94085
*/
#ifndef BEISCSI_CMDS_H
#define BEISCSI_CMDS_H
/**
* The driver sends configuration and managements command requests to the
* firmware in the BE. These requests are communicated to the processor
* using Work Request Blocks (WRBs) submitted to the MCC-WRB ring or via one
* WRB inside a MAILBOX.
* The commands are serviced by the ARM processor in the BladeEngine's MPU.
*/
struct be_sge {
u32 pa_lo;
u32 pa_hi;
u32 len;
};
#define MCC_WRB_SGE_CNT_SHIFT 3 /* bits 3 - 7 of dword 0 */
#define MCC_WRB_SGE_CNT_MASK 0x1F /* bits 3 - 7 of dword 0 */
struct be_mcc_wrb {
u32 embedded; /* dword 0 */
u32 payload_length; /* dword 1 */
u32 tag0; /* dword 2 */
u32 tag1; /* dword 3 */
u32 rsvd; /* dword 4 */
union {
u8 embedded_payload[236]; /* used by embedded cmds */
struct be_sge sgl[19]; /* used by non-embedded cmds */
} payload;
};
#define CQE_FLAGS_VALID_MASK (1 << 31)
#define CQE_FLAGS_ASYNC_MASK (1 << 30)
/* Completion Status */
#define MCC_STATUS_SUCCESS 0x0
#define CQE_STATUS_COMPL_MASK 0xFFFF
#define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */
#define CQE_STATUS_EXTD_MASK 0xFFFF
#define CQE_STATUS_EXTD_SHIFT 0 /* bits 0 - 15 */
struct be_mcc_compl {
u32 status; /* dword 0 */
u32 tag0; /* dword 1 */
u32 tag1; /* dword 2 */
u32 flags; /* dword 3 */
};
/********* Mailbox door bell *************/
/**
* Used for driver communication with the FW.
* The software must write this register twice to post any command. First,
* it writes the register with hi=1 and the upper bits of the physical address
* for the MAILBOX structure. Software must poll the ready bit until this
* is acknowledged. Then, sotware writes the register with hi=0 with the lower
* bits in the address. It must poll the ready bit until the command is
* complete. Upon completion, the MAILBOX will contain a valid completion
* queue entry.
*/
#define MPU_MAILBOX_DB_OFFSET 0x160
#define MPU_MAILBOX_DB_RDY_MASK 0x1 /* bit 0 */
#define MPU_MAILBOX_DB_HI_MASK 0x2 /* bit 1 */
/********** MPU semphore ******************/
#define MPU_EP_SEMAPHORE_OFFSET 0xac
#define EP_SEMAPHORE_POST_STAGE_MASK 0x0000FFFF
#define EP_SEMAPHORE_POST_ERR_MASK 0x1
#define EP_SEMAPHORE_POST_ERR_SHIFT 31
/********** MCC door bell ************/
#define DB_MCCQ_OFFSET 0x140
#define DB_MCCQ_RING_ID_MASK 0x7FF /* bits 0 - 10 */
/* Number of entries posted */
#define DB_MCCQ_NUM_POSTED_SHIFT 16 /* bits 16 - 29 */
/* MPU semphore POST stage values */
#define POST_STAGE_ARMFW_RDY 0xc000 /* FW is done with POST */
/**
* When the async bit of mcc_compl is set, the last 4 bytes of
* mcc_compl is interpreted as follows:
*/
#define ASYNC_TRAILER_EVENT_CODE_SHIFT 8 /* bits 8 - 15 */
#define ASYNC_TRAILER_EVENT_CODE_MASK 0xFF
#define ASYNC_EVENT_CODE_LINK_STATE 0x1
struct be_async_event_trailer {
u32 code;
};
enum {
ASYNC_EVENT_LINK_DOWN = 0x0,
ASYNC_EVENT_LINK_UP = 0x1
};
/**
* When the event code of an async trailer is link-state, the mcc_compl
* must be interpreted as follows
*/
struct be_async_event_link_state {
u8 physical_port;
u8 port_link_status;
u8 port_duplex;
u8 port_speed;
u8 port_fault;
u8 rsvd0[7];
struct be_async_event_trailer trailer;
} __packed;
struct be_mcc_mailbox {
struct be_mcc_wrb wrb;
struct be_mcc_compl compl;
};
/* Type of subsystems supported by FW */
#define CMD_SUBSYSTEM_COMMON 0x1
#define CMD_SUBSYSTEM_ISCSI 0x2
#define CMD_SUBSYSTEM_ETH 0x3
#define CMD_SUBSYSTEM_ISCSI_INI 0x6
#define CMD_COMMON_TCP_UPLOAD 0x1
/**
* List of common opcodes subsystem CMD_SUBSYSTEM_COMMON
* These opcodes are unique for each subsystem defined above
*/
#define OPCODE_COMMON_CQ_CREATE 12
#define OPCODE_COMMON_EQ_CREATE 13
#define OPCODE_COMMON_MCC_CREATE 21
#define OPCODE_COMMON_GET_CNTL_ATTRIBUTES 32
#define OPCODE_COMMON_GET_FW_VERSION 35
#define OPCODE_COMMON_MODIFY_EQ_DELAY 41
#define OPCODE_COMMON_FIRMWARE_CONFIG 42
#define OPCODE_COMMON_MCC_DESTROY 53
#define OPCODE_COMMON_CQ_DESTROY 54
#define OPCODE_COMMON_EQ_DESTROY 55
#define OPCODE_COMMON_QUERY_FIRMWARE_CONFIG 58
#define OPCODE_COMMON_FUNCTION_RESET 61
/**
* LIST of opcodes that are common between Initiator and Target
* used by CMD_SUBSYSTEM_ISCSI
* These opcodes are unique for each subsystem defined above
*/
#define OPCODE_COMMON_ISCSI_CFG_POST_SGL_PAGES 2
#define OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES 3
#define OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG 7
#define OPCODE_COMMON_ISCSI_SET_FRAGNUM_BITS_FOR_SGL_CRA 61
#define OPCODE_COMMON_ISCSI_DEFQ_CREATE 64
#define OPCODE_COMMON_ISCSI_DEFQ_DESTROY 65
#define OPCODE_COMMON_ISCSI_WRBQ_CREATE 66
#define OPCODE_COMMON_ISCSI_WRBQ_DESTROY 67
struct be_cmd_req_hdr {
u8 opcode; /* dword 0 */
u8 subsystem; /* dword 0 */
u8 port_number; /* dword 0 */
u8 domain; /* dword 0 */
u32 timeout; /* dword 1 */
u32 request_length; /* dword 2 */
u32 rsvd; /* dword 3 */
};
struct be_cmd_resp_hdr {
u32 info; /* dword 0 */
u32 status; /* dword 1 */
u32 response_length; /* dword 2 */
u32 actual_resp_len; /* dword 3 */
};
struct phys_addr {
u32 lo;
u32 hi;
};
/**************************
* BE Command definitions *
**************************/
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte - used to calculate offset/shift/mask of each field
*/
struct amap_eq_context {
u8 cidx[13]; /* dword 0 */
u8 rsvd0[3]; /* dword 0 */
u8 epidx[13]; /* dword 0 */
u8 valid; /* dword 0 */
u8 rsvd1; /* dword 0 */
u8 size; /* dword 0 */
u8 pidx[13]; /* dword 1 */
u8 rsvd2[3]; /* dword 1 */
u8 pd[10]; /* dword 1 */
u8 count[3]; /* dword 1 */
u8 solevent; /* dword 1 */
u8 stalled; /* dword 1 */
u8 armed; /* dword 1 */
u8 rsvd3[4]; /* dword 2 */
u8 func[8]; /* dword 2 */
u8 rsvd4; /* dword 2 */
u8 delaymult[10]; /* dword 2 */
u8 rsvd5[2]; /* dword 2 */
u8 phase[2]; /* dword 2 */
u8 nodelay; /* dword 2 */
u8 rsvd6[4]; /* dword 2 */
u8 rsvd7[32]; /* dword 3 */
} __packed;
struct be_cmd_req_eq_create {
struct be_cmd_req_hdr hdr; /* dw[4] */
u16 num_pages; /* sword */
u16 rsvd0; /* sword */
u8 context[sizeof(struct amap_eq_context) / 8]; /* dw[4] */
struct phys_addr pages[8];
} __packed;
struct be_cmd_resp_eq_create {
struct be_cmd_resp_hdr resp_hdr;
u16 eq_id; /* sword */
u16 rsvd0; /* sword */
} __packed;
struct mac_addr {
u16 size_of_struct;
u8 addr[ETH_ALEN];
} __packed;
struct be_cmd_req_mac_query {
struct be_cmd_req_hdr hdr;
u8 type;
u8 permanent;
u16 if_id;
} __packed;
struct be_cmd_resp_mac_query {
struct be_cmd_resp_hdr hdr;
struct mac_addr mac;
};
/******************** Create CQ ***************************/
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte - used to calculate offset/shift/mask of each field
*/
struct amap_cq_context {
u8 cidx[11]; /* dword 0 */
u8 rsvd0; /* dword 0 */
u8 coalescwm[2]; /* dword 0 */
u8 nodelay; /* dword 0 */
u8 epidx[11]; /* dword 0 */
u8 rsvd1; /* dword 0 */
u8 count[2]; /* dword 0 */
u8 valid; /* dword 0 */
u8 solevent; /* dword 0 */
u8 eventable; /* dword 0 */
u8 pidx[11]; /* dword 1 */
u8 rsvd2; /* dword 1 */
u8 pd[10]; /* dword 1 */
u8 eqid[8]; /* dword 1 */
u8 stalled; /* dword 1 */
u8 armed; /* dword 1 */
u8 rsvd3[4]; /* dword 2 */
u8 func[8]; /* dword 2 */
u8 rsvd4[20]; /* dword 2 */
u8 rsvd5[32]; /* dword 3 */
} __packed;
struct be_cmd_req_cq_create {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u16 rsvd0;
u8 context[sizeof(struct amap_cq_context) / 8];
struct phys_addr pages[4];
} __packed;
struct be_cmd_resp_cq_create {
struct be_cmd_resp_hdr hdr;
u16 cq_id;
u16 rsvd0;
} __packed;
/******************** Create MCCQ ***************************/
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte - used to calculate offset/shift/mask of each field
*/
struct amap_mcc_context {
u8 con_index[14];
u8 rsvd0[2];
u8 ring_size[4];
u8 fetch_wrb;
u8 fetch_r2t;
u8 cq_id[10];
u8 prod_index[14];
u8 fid[8];
u8 pdid[9];
u8 valid;
u8 rsvd1[32];
u8 rsvd2[32];
} __packed;
struct be_cmd_req_mcc_create {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u16 rsvd0;
u8 context[sizeof(struct amap_mcc_context) / 8];
struct phys_addr pages[8];
} __packed;
struct be_cmd_resp_mcc_create {
struct be_cmd_resp_hdr hdr;
u16 id;
u16 rsvd0;
} __packed;
/******************** Q Destroy ***************************/
/* Type of Queue to be destroyed */
enum {
QTYPE_EQ = 1,
QTYPE_CQ,
QTYPE_MCCQ,
QTYPE_WRBQ,
QTYPE_DPDUQ,
QTYPE_SGL
};
struct be_cmd_req_q_destroy {
struct be_cmd_req_hdr hdr;
u16 id;
u16 bypass_flush; /* valid only for rx q destroy */
} __packed;
struct macaddr {
u8 byte[ETH_ALEN];
};
struct be_cmd_req_mcast_mac_config {
struct be_cmd_req_hdr hdr;
u16 num_mac;
u8 promiscuous;
u8 interface_id;
struct macaddr mac[32];
} __packed;
static inline void *embedded_payload(struct be_mcc_wrb *wrb)
{
return wrb->payload.embedded_payload;
}
static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb)
{
return &wrb->payload.sgl[0];
}
/******************** Modify EQ Delay *******************/
struct be_cmd_req_modify_eq_delay {
struct be_cmd_req_hdr hdr;
u32 num_eq;
struct {
u32 eq_id;
u32 phase;
u32 delay_multiplier;
} delay[8];
} __packed;
/******************** Get MAC ADDR *******************/
#define ETH_ALEN 6
struct be_cmd_req_get_mac_addr {
struct be_cmd_req_hdr hdr;
u32 nic_port_count;
u32 speed;
u32 max_speed;
u32 link_state;
u32 max_frame_size;
u16 size_of_structure;
u8 mac_address[ETH_ALEN];
u32 rsvd[23];
};
struct be_cmd_resp_get_mac_addr {
struct be_cmd_resp_hdr hdr;
u32 nic_port_count;
u32 speed;
u32 max_speed;
u32 link_state;
u32 max_frame_size;
u16 size_of_structure;
u8 mac_address[6];
u32 rsvd[23];
};
int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *eq, int eq_delay);
int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *cq, struct be_queue_info *eq,
bool sol_evts, bool no_delay,
int num_cqe_dma_coalesce);
int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
int type);
int be_poll_mcc(struct be_ctrl_info *ctrl);
unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl);
int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr);
/*ISCSI Functuions */
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem);
int be_mbox_notify(struct be_ctrl_info *ctrl);
int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
struct be_queue_info *cq,
struct be_queue_info *dq, int length,
int entry_size);
int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl,
struct be_dma_mem *q_mem, u32 page_offset,
u32 num_pages);
int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
struct be_queue_info *wrbq);
struct be_default_pdu_context {
u32 dw[4];
} __packed;
struct amap_be_default_pdu_context {
u8 dbuf_cindex[13]; /* dword 0 */
u8 rsvd0[3]; /* dword 0 */
u8 ring_size[4]; /* dword 0 */
u8 ring_state[4]; /* dword 0 */
u8 rsvd1[8]; /* dword 0 */
u8 dbuf_pindex[13]; /* dword 1 */
u8 rsvd2; /* dword 1 */
u8 pci_func_id[8]; /* dword 1 */
u8 rx_pdid[9]; /* dword 1 */
u8 rx_pdid_valid; /* dword 1 */
u8 default_buffer_size[16]; /* dword 2 */
u8 cq_id_recv[10]; /* dword 2 */
u8 rx_pdid_not_valid; /* dword 2 */
u8 rsvd3[5]; /* dword 2 */
u8 rsvd4[32]; /* dword 3 */
} __packed;
struct be_defq_create_req {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u8 ulp_num;
u8 rsvd0;
struct be_default_pdu_context context;
struct phys_addr pages[8];
} __packed;
struct be_defq_create_resp {
struct be_cmd_req_hdr hdr;
u16 id;
u16 rsvd0;
} __packed;
struct be_post_sgl_pages_req {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u16 page_offset;
u32 rsvd0;
struct phys_addr pages[26];
u32 rsvd1;
} __packed;
struct be_wrbq_create_req {
struct be_cmd_req_hdr hdr;
u16 num_pages;
u8 ulp_num;
u8 rsvd0;
struct phys_addr pages[8];
} __packed;
struct be_wrbq_create_resp {
struct be_cmd_resp_hdr resp_hdr;
u16 cid;
u16 rsvd0;
} __packed;
#define SOL_CID_MASK 0x0000FFC0
#define SOL_CODE_MASK 0x0000003F
#define SOL_WRB_INDEX_MASK 0x00FF0000
#define SOL_CMD_WND_MASK 0xFF000000
#define SOL_RES_CNT_MASK 0x7FFFFFFF
#define SOL_EXP_CMD_SN_MASK 0xFFFFFFFF
#define SOL_HW_STS_MASK 0x000000FF
#define SOL_STS_MASK 0x0000FF00
#define SOL_RESP_MASK 0x00FF0000
#define SOL_FLAGS_MASK 0x7F000000
#define SOL_S_MASK 0x80000000
struct sol_cqe {
u32 dw[4];
};
struct amap_sol_cqe {
u8 hw_sts[8]; /* dword 0 */
u8 i_sts[8]; /* dword 0 */
u8 i_resp[8]; /* dword 0 */
u8 i_flags[7]; /* dword 0 */
u8 s; /* dword 0 */
u8 i_exp_cmd_sn[32]; /* dword 1 */
u8 code[6]; /* dword 2 */
u8 cid[10]; /* dword 2 */
u8 wrb_index[8]; /* dword 2 */
u8 i_cmd_wnd[8]; /* dword 2 */
u8 i_res_cnt[31]; /* dword 3 */
u8 valid; /* dword 3 */
} __packed;
/**
* Post WRB Queue Doorbell Register used by the host Storage
* stack to notify the
* controller of a posted Work Request Block
*/
#define DB_WRB_POST_CID_MASK 0x3FF /* bits 0 - 9 */
#define DB_DEF_PDU_WRB_INDEX_MASK 0xFF /* bits 0 - 9 */
#define DB_DEF_PDU_WRB_INDEX_SHIFT 16
#define DB_DEF_PDU_NUM_POSTED_SHIFT 24
struct fragnum_bits_for_sgl_cra_in {
struct be_cmd_req_hdr hdr;
u32 num_bits;
} __packed;
struct iscsi_cleanup_req {
struct be_cmd_req_hdr hdr;
u16 chute;
u8 hdr_ring_id;
u8 data_ring_id;
} __packed;
struct eq_delay {
u32 eq_id;
u32 phase;
u32 delay_multiplier;
} __packed;
struct be_eq_delay_params_in {
struct be_cmd_req_hdr hdr;
u32 num_eq;
struct eq_delay delay[8];
} __packed;
struct ip_address_format {
u16 size_of_structure;
u8 reserved;
u8 ip_type;
u8 ip_address[16];
u32 rsvd0;
} __packed;
struct tcp_connect_and_offload_in {
struct be_cmd_req_hdr hdr;
struct ip_address_format ip_address;
u16 tcp_port;
u16 cid;
u16 cq_id;
u16 defq_id;
struct phys_addr dataout_template_pa;
u16 hdr_ring_id;
u16 data_ring_id;
u8 do_offload;
u8 rsvd0[3];
} __packed;
struct tcp_connect_and_offload_out {
struct be_cmd_resp_hdr hdr;
u32 connection_handle;
u16 cid;
u16 rsvd0;
} __packed;
struct be_mcc_wrb_context {
struct MCC_WRB *wrb;
int *users_final_status;
} __packed;
#define DB_DEF_PDU_RING_ID_MASK 0x3FF /* bits 0 - 9 */
#define DB_DEF_PDU_CQPROC_MASK 0x3FFF /* bits 0 - 9 */
#define DB_DEF_PDU_REARM_SHIFT 14
#define DB_DEF_PDU_EVENT_SHIFT 15
#define DB_DEF_PDU_CQPROC_SHIFT 16
struct dmsg_cqe {
u32 dw[4];
} __packed;
struct tcp_upload_params_in {
struct be_cmd_req_hdr hdr;
u16 id;
u16 upload_type;
u32 reset_seq;
} __packed;
struct tcp_upload_params_out {
u32 dw[32];
} __packed;
union tcp_upload_params {
struct tcp_upload_params_in request;
struct tcp_upload_params_out response;
} __packed;
struct be_ulp_fw_cfg {
u32 ulp_mode;
u32 etx_base;
u32 etx_count;
u32 sq_base;
u32 sq_count;
u32 rq_base;
u32 rq_count;
u32 dq_base;
u32 dq_count;
u32 lro_base;
u32 lro_count;
u32 icd_base;
u32 icd_count;
};
struct be_fw_cfg {
struct be_cmd_req_hdr hdr;
u32 be_config_number;
u32 asic_revision;
u32 phys_port;
u32 function_mode;
struct be_ulp_fw_cfg ulp[2];
u32 function_caps;
} __packed;
#define CMD_ISCSI_COMMAND_INVALIDATE 1
#define ISCSI_OPCODE_SCSI_DATA_OUT 5
#define OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD 70
#define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41
#define OPCODE_COMMON_MODIFY_EQ_DELAY 41
#define OPCODE_COMMON_ISCSI_CLEANUP 59
#define OPCODE_COMMON_TCP_UPLOAD 56
#define OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS 1
/* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */
#define CMD_ISCSI_CONNECTION_INVALIDATE 1
#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 2
#define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42
#define INI_WR_CMD 1 /* Initiator write command */
#define INI_TMF_CMD 2 /* Initiator TMF command */
#define INI_NOPOUT_CMD 3 /* Initiator; Send a NOP-OUT */
#define INI_RD_CMD 5 /* Initiator requesting to send
* a read command
*/
#define TGT_CTX_UPDT_CMD 7 /* Target context update */
#define TGT_STS_CMD 8 /* Target R2T and other BHS
* where only the status number
* need to be updated
*/
#define TGT_DATAIN_CMD 9 /* Target Data-Ins in response
* to read command
*/
#define TGT_SOS_PDU 10 /* Target:standalone status
* response
*/
#define TGT_DM_CMD 11 /* Indicates that the bhs
* preparedby
* driver should not be touched
*/
/* --- CMD_CHUTE_TYPE --- */
#define CMD_CONNECTION_CHUTE_0 1
#define CMD_CONNECTION_CHUTE_1 2
#define CMD_CONNECTION_CHUTE_2 3
#define EQ_MAJOR_CODE_COMPLETION 0
#define CMD_ISCSI_SESSION_DEL_CFG_FROM_FLASH 0
#define CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH 1
/* --- CONNECTION_UPLOAD_PARAMS --- */
/* These parameters are used to define the type of upload desired. */
#define CONNECTION_UPLOAD_GRACEFUL 1 /* Graceful upload */
#define CONNECTION_UPLOAD_ABORT_RESET 2 /* Abortive upload with
* reset
*/
#define CONNECTION_UPLOAD_ABORT 3 /* Abortive upload without
* reset
*/
#define CONNECTION_UPLOAD_ABORT_WITH_SEQ 4 /* Abortive upload with reset,
* sequence number by driver */
/* Returns byte size of given field with a structure. */
/* Returns the number of items in the field array. */
#define BE_NUMBER_OF_FIELD(_type_, _field_) \
(FIELD_SIZEOF(_type_, _field_)/sizeof((((_type_ *)0)->_field_[0])))\
/**
* Different types of iSCSI completions to host driver for both initiator
* and taget mode
* of operation.
*/
#define SOL_CMD_COMPLETE 1 /* Solicited command completed
* normally
*/
#define SOL_CMD_KILLED_DATA_DIGEST_ERR 2 /* Solicited command got
* invalidated internally due
* to Data Digest error
*/
#define CXN_KILLED_PDU_SIZE_EXCEEDS_DSL 3 /* Connection got invalidated
* internally
* due to a recieved PDU
* size > DSL
*/
#define CXN_KILLED_BURST_LEN_MISMATCH 4 /* Connection got invalidated
* internally due ti received
* PDU sequence size >
* FBL/MBL.
*/
#define CXN_KILLED_AHS_RCVD 5 /* Connection got invalidated
* internally due to a recieved
* PDU Hdr that has
* AHS */
#define CXN_KILLED_HDR_DIGEST_ERR 6 /* Connection got invalidated
* internally due to Hdr Digest
* error
*/
#define CXN_KILLED_UNKNOWN_HDR 7 /* Connection got invalidated
* internally
* due to a bad opcode in the
* pdu hdr
*/
#define CXN_KILLED_STALE_ITT_TTT_RCVD 8 /* Connection got invalidated
* internally due to a recieved
* ITT/TTT that does not belong
* to this Connection
*/
#define CXN_KILLED_INVALID_ITT_TTT_RCVD 9 /* Connection got invalidated
* internally due to recieved
* ITT/TTT value > Max
* Supported ITTs/TTTs
*/
#define CXN_KILLED_RST_RCVD 10 /* Connection got invalidated
* internally due to an
* incoming TCP RST
*/
#define CXN_KILLED_TIMED_OUT 11 /* Connection got invalidated
* internally due to timeout on
* tcp segment 12 retransmit
* attempts failed
*/
#define CXN_KILLED_RST_SENT 12 /* Connection got invalidated
* internally due to TCP RST
* sent by the Tx side
*/
#define CXN_KILLED_FIN_RCVD 13 /* Connection got invalidated
* internally due to an
* incoming TCP FIN.
*/
#define CXN_KILLED_BAD_UNSOL_PDU_RCVD 14 /* Connection got invalidated
* internally due to bad
* unsolicited PDU Unsolicited
* PDUs are PDUs with
* ITT=0xffffffff
*/
#define CXN_KILLED_BAD_WRB_INDEX_ERROR 15 /* Connection got invalidated
* internally due to bad WRB
* index.
*/
#define CXN_KILLED_OVER_RUN_RESIDUAL 16 /* Command got invalidated
* internally due to recived
* command has residual
* over run bytes.
*/
#define CXN_KILLED_UNDER_RUN_RESIDUAL 17 /* Command got invalidated
* internally due to recived
* command has residual under
* run bytes.
*/
#define CMD_KILLED_INVALID_STATSN_RCVD 18 /* Command got invalidated
* internally due to a recieved
* PDU has an invalid StatusSN
*/
#define CMD_KILLED_INVALID_R2T_RCVD 19 /* Command got invalidated
* internally due to a recieved
* an R2T with some invalid
* fields in it
*/
#define CMD_CXN_KILLED_LUN_INVALID 20 /* Command got invalidated
* internally due to received
* PDU has an invalid LUN.
*/
#define CMD_CXN_KILLED_ICD_INVALID 21 /* Command got invalidated
* internally due to the
* corresponding ICD not in a
* valid state
*/
#define CMD_CXN_KILLED_ITT_INVALID 22 /* Command got invalidated due
* to received PDU has an
* invalid ITT.
*/
#define CMD_CXN_KILLED_SEQ_OUTOFORDER 23 /* Command got invalidated due
* to received sequence buffer
* offset is out of order.
*/
#define CMD_CXN_KILLED_INVALID_DATASN_RCVD 24 /* Command got invalidated
* internally due to a
* recieved PDU has an invalid
* DataSN
*/
#define CXN_INVALIDATE_NOTIFY 25 /* Connection invalidation
* completion notify.
*/
#define CXN_INVALIDATE_INDEX_NOTIFY 26 /* Connection invalidation
* completion
* with data PDU index.
*/
#define CMD_INVALIDATED_NOTIFY 27 /* Command invalidation
* completionnotifify.
*/
#define UNSOL_HDR_NOTIFY 28 /* Unsolicited header notify.*/
#define UNSOL_DATA_NOTIFY 29 /* Unsolicited data notify.*/
#define UNSOL_DATA_DIGEST_ERROR_NOTIFY 30 /* Unsolicited data digest
* error notify.
*/
#define DRIVERMSG_NOTIFY 31 /* TCP acknowledge based
* notification.
*/
#define CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN 32 /* Connection got invalidated
* internally due to command
* and data are not on same
* connection.
*/
#define SOL_CMD_KILLED_DIF_ERR 33 /* Solicited command got
* invalidated internally due
* to DIF error
*/
#define CXN_KILLED_SYN_RCVD 34 /* Connection got invalidated
* internally due to incoming
* TCP SYN
*/
#define CXN_KILLED_IMM_DATA_RCVD 35 /* Connection got invalidated
* internally due to an
* incoming Unsolicited PDU
* that has immediate data on
* the cxn
*/
void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
bool embedded, u8 sge_cnt);
void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
u8 subsystem, u8 opcode, int cmd_len);
#endif /* !BEISCSI_CMDS_H */

View File

@ -0,0 +1,646 @@
/**
* Copyright (C) 2005 - 2009 ServerEngines
* 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 (jayamohank@serverengines.com)
*
* Contact Information:
* linux-drivers@serverengines.com
*
* ServerEngines
* 209 N. Fair Oaks Ave
* Sunnyvale, CA 94085
*
*/
#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_iscsi.h"
extern struct iscsi_transport beiscsi_iscsi_transport;
/**
* beiscsi_session_create - creates a new iscsi session
* @cmds_max: max commands supported
* @qdepth: max queue depth supported
* @initial_cmdsn: initial iscsi CMDSN
*/
struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
u16 cmds_max,
u16 qdepth,
u32 initial_cmdsn)
{
struct Scsi_Host *shost;
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_cls_session *cls_session;
struct iscsi_session *sess;
struct beiscsi_hba *phba;
struct iscsi_task *task;
struct beiscsi_io_task *io_task;
unsigned int max_size, num_cmd;
dma_addr_t bus_add;
u64 pa_addr;
void *vaddr;
SE_DEBUG(DBG_LVL_8, "In beiscsi_session_create\n");
if (!ep) {
SE_DEBUG(DBG_LVL_1, "beiscsi_session_create: invalid ep \n");
return NULL;
}
beiscsi_ep = ep->dd_data;
phba = beiscsi_ep->phba;
shost = phba->shost;
if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) {
shost_printk(KERN_ERR, shost, "Cannot handle %d cmds."
"Max cmds per session supported is %d. Using %d. "
"\n", cmds_max,
beiscsi_ep->phba->params.wrbs_per_cxn,
beiscsi_ep->phba->params.wrbs_per_cxn);
cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn;
}
cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
shost, cmds_max,
sizeof(struct beiscsi_io_task),
initial_cmdsn, ISCSI_MAX_TARGET);
if (!cls_session)
return NULL;
sess = cls_session->dd_data;
max_size = ALIGN(sizeof(struct be_cmd_bhs), 64) * sess->cmds_max;
vaddr = pci_alloc_consistent(phba->pcidev, max_size, &bus_add);
pa_addr = (__u64) bus_add;
for (num_cmd = 0; num_cmd < sess->cmds_max; num_cmd++) {
task = sess->cmds[num_cmd];
io_task = task->dd_data;
io_task->cmd_bhs = vaddr;
io_task->bhs_pa.u.a64.address = pa_addr;
io_task->alloc_size = max_size;
vaddr += ALIGN(sizeof(struct be_cmd_bhs), 64);
pa_addr += ALIGN(sizeof(struct be_cmd_bhs), 64);
}
return cls_session;
}
/**
* beiscsi_session_destroy - destroys iscsi session
* @cls_session: pointer to iscsi cls session
*
* Destroys iSCSI session instance and releases
* resources allocated for it.
*/
void beiscsi_session_destroy(struct iscsi_cls_session *cls_session)
{
struct iscsi_task *task;
struct beiscsi_io_task *io_task;
struct iscsi_session *sess = cls_session->dd_data;
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
struct beiscsi_hba *phba = iscsi_host_priv(shost);
task = sess->cmds[0];
io_task = task->dd_data;
pci_free_consistent(phba->pcidev,
io_task->alloc_size,
io_task->cmd_bhs,
io_task->bhs_pa.u.a64.address);
iscsi_session_teardown(cls_session);
}
/**
* beiscsi_conn_create - create an instance of iscsi connection
* @cls_session: ptr to iscsi_cls_session
* @cid: iscsi cid
*/
struct iscsi_cls_conn *
beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid)
{
struct beiscsi_hba *phba;
struct Scsi_Host *shost;
struct iscsi_cls_conn *cls_conn;
struct beiscsi_conn *beiscsi_conn;
struct iscsi_conn *conn;
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_create ,cid"
"from iscsi layer=%d\n", cid);
shost = iscsi_session_to_shost(cls_session);
phba = iscsi_host_priv(shost);
cls_conn = iscsi_conn_setup(cls_session, sizeof(*beiscsi_conn), cid);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
beiscsi_conn = conn->dd_data;
beiscsi_conn->ep = NULL;
beiscsi_conn->phba = phba;
beiscsi_conn->conn = conn;
return cls_conn;
}
/**
* beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table
* @beiscsi_conn: The pointer to beiscsi_conn structure
* @phba: The phba instance
* @cid: The cid to free
*/
static int beiscsi_bindconn_cid(struct beiscsi_hba *phba,
struct beiscsi_conn *beiscsi_conn,
unsigned int cid)
{
if (phba->conn_table[cid]) {
SE_DEBUG(DBG_LVL_1,
"Connection table already occupied. Detected clash\n");
return -EINVAL;
} else {
SE_DEBUG(DBG_LVL_8, "phba->conn_table[%d]=%p(beiscsi_conn) \n",
cid, beiscsi_conn);
phba->conn_table[cid] = beiscsi_conn;
}
return 0;
}
/**
* beiscsi_conn_bind - Binds iscsi session/connection with TCP connection
* @cls_session: pointer to iscsi cls session
* @cls_conn: pointer to iscsi cls conn
* @transport_fd: EP handle(64 bit)
*
* This function binds the TCP Conn with iSCSI Connection and Session.
*/
int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn,
u64 transport_fd, int is_leading)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct Scsi_Host *shost =
(struct Scsi_Host *)iscsi_session_to_shost(cls_session);
struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost);
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_endpoint *ep;
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_bind\n");
ep = iscsi_lookup_endpoint(transport_fd);
if (!ep)
return -EINVAL;
beiscsi_ep = ep->dd_data;
if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
return -EINVAL;
if (beiscsi_ep->phba != phba) {
SE_DEBUG(DBG_LVL_8,
"beiscsi_ep->hba=%p not equal to phba=%p \n",
beiscsi_ep->phba, phba);
return -EEXIST;
}
beiscsi_conn->beiscsi_conn_cid = beiscsi_ep->ep_cid;
beiscsi_conn->ep = beiscsi_ep;
beiscsi_ep->conn = beiscsi_conn;
SE_DEBUG(DBG_LVL_8, "beiscsi_conn=%p conn=%p ep_cid=%d \n",
beiscsi_conn, conn, beiscsi_ep->ep_cid);
return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid);
}
/**
* beiscsi_conn_get_param - get the iscsi parameter
* @cls_conn: pointer to iscsi cls conn
* @param: parameter type identifier
* @buf: buffer pointer
*
* returns iscsi parameter
*/
int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
{
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_conn *conn = cls_conn->dd_data;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
int len = 0;
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep) {
SE_DEBUG(DBG_LVL_1,
"In beiscsi_conn_get_param , no beiscsi_ep\n");
return -1;
}
switch (param) {
case ISCSI_PARAM_CONN_PORT:
len = sprintf(buf, "%hu\n", beiscsi_ep->dst_tcpport);
break;
case ISCSI_PARAM_CONN_ADDRESS:
if (beiscsi_ep->ip_type == BE2_IPV4)
len = sprintf(buf, "%pI4\n", &beiscsi_ep->dst_addr);
else
len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr);
break;
default:
return iscsi_conn_get_param(cls_conn, param, buf);
}
return len;
}
int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session;
int ret;
ret = iscsi_set_param(cls_conn, param, buf, buflen);
if (ret)
return ret;
/*
* If userspace tried to set the value to higher than we can
* support override here.
*/
switch (param) {
case ISCSI_PARAM_FIRST_BURST:
if (session->first_burst > 8192)
session->first_burst = 8192;
break;
case ISCSI_PARAM_MAX_RECV_DLENGTH:
if (conn->max_recv_dlength > 65536)
conn->max_recv_dlength = 65536;
break;
case ISCSI_PARAM_MAX_BURST:
if (session->first_burst > 262144)
session->first_burst = 262144;
break;
default:
return 0;
}
return 0;
}
/**
* beiscsi_get_host_param - get the iscsi parameter
* @shost: pointer to scsi_host structure
* @param: parameter type identifier
* @buf: buffer pointer
*
* returns host parameter
*/
int beiscsi_get_host_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf)
{
struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost);
int len = 0;
switch (param) {
case ISCSI_HOST_PARAM_HWADDRESS:
be_cmd_get_mac_addr(&phba->ctrl, phba->mac_address);
len = sysfs_format_mac(buf, phba->mac_address, ETH_ALEN);
break;
default:
return iscsi_host_get_param(shost, param, buf);
}
return len;
}
/**
* beiscsi_conn_get_stats - get the iscsi stats
* @cls_conn: pointer to iscsi cls conn
* @stats: pointer to iscsi_stats structure
*
* returns iscsi stats
*/
void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn,
struct iscsi_stats *stats)
{
struct iscsi_conn *conn = cls_conn->dd_data;
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_stats\n");
stats->txdata_octets = conn->txdata_octets;
stats->rxdata_octets = conn->rxdata_octets;
stats->dataout_pdus = conn->dataout_pdus_cnt;
stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
stats->datain_pdus = conn->datain_pdus_cnt;
stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
stats->r2t_pdus = conn->r2t_pdus_cnt;
stats->digest_err = 0;
stats->timeout_err = 0;
stats->custom_length = 0;
strcpy(stats->custom[0].desc, "eh_abort_cnt");
stats->custom[0].value = conn->eh_abort_cnt;
}
/**
* beiscsi_set_params_for_offld - get the parameters for offload
* @beiscsi_conn: pointer to beiscsi_conn
* @params: pointer to offload_params structure
*/
static void beiscsi_set_params_for_offld(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_offload_params *params)
{
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
AMAP_SET_BITS(struct amap_beiscsi_offload_params, max_burst_length,
params, session->max_burst);
AMAP_SET_BITS(struct amap_beiscsi_offload_params,
max_send_data_segment_length, params,
conn->max_xmit_dlength);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, first_burst_length,
params, session->first_burst);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, erl, params,
session->erl);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, dde, params,
conn->datadgst_en);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, hde, params,
conn->hdrdgst_en);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, ir2t, params,
session->initial_r2t_en);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, imd, params,
session->imm_data_en);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, exp_statsn, params,
(conn->exp_statsn - 1));
}
/**
* beiscsi_conn_start - offload of session to chip
* @cls_conn: pointer to beiscsi_conn
*/
int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_endpoint *beiscsi_ep;
struct beiscsi_offload_params params;
struct iscsi_session *session = conn->session;
struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
struct beiscsi_hba *phba = iscsi_host_priv(shost);
memset(&params, 0, sizeof(struct beiscsi_offload_params));
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep)
SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n");
free_mgmt_sgl_handle(phba, beiscsi_conn->plogin_sgl_handle);
beiscsi_conn->login_in_progress = 0;
beiscsi_set_params_for_offld(beiscsi_conn, &params);
beiscsi_offload_connection(beiscsi_conn, &params);
iscsi_conn_start(cls_conn);
return 0;
}
/**
* beiscsi_get_cid - Allocate a cid
* @phba: The phba instance
*/
static int beiscsi_get_cid(struct beiscsi_hba *phba)
{
unsigned short cid = 0xFFFF;
if (!phba->avlbl_cids)
return cid;
cid = phba->cid_array[phba->cid_alloc++];
if (phba->cid_alloc == phba->params.cxns_per_ctrl)
phba->cid_alloc = 0;
phba->avlbl_cids--;
return cid;
}
/**
* beiscsi_open_conn - Ask FW to open a TCP connection
* @ep: endpoint to be used
* @src_addr: The source IP address
* @dst_addr: The Destination IP address
*
* Asks the FW to open a TCP connection
*/
static int beiscsi_open_conn(struct iscsi_endpoint *ep,
struct sockaddr *src_addr,
struct sockaddr *dst_addr, int non_blocking)
{
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
struct beiscsi_hba *phba = beiscsi_ep->phba;
int ret = -1;
beiscsi_ep->ep_cid = beiscsi_get_cid(phba);
if (beiscsi_ep->ep_cid == 0xFFFF) {
SE_DEBUG(DBG_LVL_1, "No free cid available\n");
return ret;
}
SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn, ep_cid=%d ",
beiscsi_ep->ep_cid);
phba->ep_array[beiscsi_ep->ep_cid] = ep;
if (beiscsi_ep->ep_cid >
(phba->fw_config.iscsi_cid_start + phba->params.cxns_per_ctrl)) {
SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n");
return ret;
}
beiscsi_ep->cid_vld = 0;
return mgmt_open_connection(phba, dst_addr, beiscsi_ep);
}
/**
* beiscsi_put_cid - Free the cid
* @phba: The phba for which the cid is being freed
* @cid: The cid to free
*/
static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid)
{
phba->avlbl_cids++;
phba->cid_array[phba->cid_free++] = cid;
if (phba->cid_free == phba->params.cxns_per_ctrl)
phba->cid_free = 0;
}
/**
* beiscsi_free_ep - free endpoint
* @ep: pointer to iscsi endpoint structure
*/
static void beiscsi_free_ep(struct iscsi_endpoint *ep)
{
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
struct beiscsi_hba *phba = beiscsi_ep->phba;
beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
beiscsi_ep->phba = NULL;
iscsi_destroy_endpoint(ep);
}
/**
* beiscsi_ep_connect - Ask chip to create TCP Conn
* @scsi_host: Pointer to scsi_host structure
* @dst_addr: The IP address of Target
* @non_blocking: blocking or non-blocking call
*
* This routines first asks chip to create a connection and then allocates an EP
*/
struct iscsi_endpoint *
beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
int non_blocking)
{
struct beiscsi_hba *phba;
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_endpoint *ep;
int ret;
SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_connect \n");
if (shost)
phba = iscsi_host_priv(shost);
else {
ret = -ENXIO;
SE_DEBUG(DBG_LVL_1, "shost is NULL \n");
return ERR_PTR(ret);
}
ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint));
if (!ep) {
ret = -ENOMEM;
return ERR_PTR(ret);
}
beiscsi_ep = ep->dd_data;
beiscsi_ep->phba = phba;
if (beiscsi_open_conn(ep, NULL, dst_addr, non_blocking)) {
SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n");
ret = -ENOMEM;
goto free_ep;
}
return ep;
free_ep:
beiscsi_free_ep(ep);
return ERR_PTR(ret);
}
/**
* beiscsi_ep_poll - Poll to see if connection is established
* @ep: endpoint to be used
* @timeout_ms: timeout specified in millisecs
*
* Poll to see if TCP connection established
*/
int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
{
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_poll\n");
if (beiscsi_ep->cid_vld == 1)
return 1;
else
return 0;
}
/**
* beiscsi_close_conn - Upload the connection
* @ep: The iscsi endpoint
* @flag: The type of connection closure
*/
static int beiscsi_close_conn(struct iscsi_endpoint *ep, int flag)
{
int ret = 0;
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
struct beiscsi_hba *phba = beiscsi_ep->phba;
if (MGMT_STATUS_SUCCESS !=
mgmt_upload_connection(phba, beiscsi_ep->ep_cid,
CONNECTION_UPLOAD_GRACEFUL)) {
SE_DEBUG(DBG_LVL_8, "upload failed for cid 0x%x",
beiscsi_ep->ep_cid);
ret = -1;
}
return ret;
}
/**
* beiscsi_ep_disconnect - Tears down the TCP connection
* @ep: endpoint to be used
*
* Tears down the TCP connection
*/
void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
{
struct beiscsi_conn *beiscsi_conn;
struct beiscsi_endpoint *beiscsi_ep;
struct beiscsi_hba *phba;
int flag = 0;
beiscsi_ep = ep->dd_data;
phba = beiscsi_ep->phba;
SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect\n");
if (beiscsi_ep->conn) {
beiscsi_conn = beiscsi_ep->conn;
iscsi_suspend_queue(beiscsi_conn->conn);
beiscsi_close_conn(ep, flag);
}
beiscsi_free_ep(ep);
}
/**
* beiscsi_unbind_conn_to_cid - Unbind the beiscsi_conn from phba conn table
* @phba: The phba instance
* @cid: The cid to free
*/
static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba,
unsigned int cid)
{
if (phba->conn_table[cid])
phba->conn_table[cid] = NULL;
else {
SE_DEBUG(DBG_LVL_8, "Connection table Not occupied. \n");
return -EINVAL;
}
return 0;
}
/**
* beiscsi_conn_stop - Invalidate and stop the connection
* @cls_conn: pointer to get iscsi_conn
* @flag: The type of connection closure
*/
void beiscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_session *session = conn->session;
struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
struct beiscsi_hba *phba = iscsi_host_priv(shost);
unsigned int status;
unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH;
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop\n");
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep) {
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop , no beiscsi_ep\n");
return;
}
status = mgmt_invalidate_connection(phba, beiscsi_ep,
beiscsi_ep->ep_cid, 1,
savecfg_flag);
if (status != MGMT_STATUS_SUCCESS) {
SE_DEBUG(DBG_LVL_1,
"mgmt_invalidate_connection Failed for cid=%d \n",
beiscsi_ep->ep_cid);
}
beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid);
iscsi_conn_stop(cls_conn, flag);
}

View File

@ -0,0 +1,75 @@
/**
* Copyright (C) 2005 - 2009 ServerEngines
* 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 (jayamohank@serverengines.com)
*
* Contact Information:
* linux-drivers@serverengines.com
*
* ServerEngines
* 209 N. Fair Oaks Ave
* Sunnyvale, CA 94085
*
*/
#ifndef _BE_ISCSI_
#define _BE_ISCSI_
#include "be_main.h"
#include "be_mgmt.h"
#define BE2_IPV4 0x1
#define BE2_IPV6 0x10
void beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_offload_params *params);
void beiscsi_offload_iscsi(struct beiscsi_hba *phba, struct iscsi_conn *conn,
struct beiscsi_conn *beiscsi_conn,
unsigned int fw_handle);
struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
uint16_t cmds_max,
uint16_t qdepth,
uint32_t initial_cmdsn);
void beiscsi_session_destroy(struct iscsi_cls_session *cls_session);
struct iscsi_cls_conn *beiscsi_conn_create(struct iscsi_cls_session
*cls_session, uint32_t cid);
int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn,
uint64_t transport_fd, int is_leading);
int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf);
int beiscsi_get_host_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf);
int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen);
int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn);
void beiscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag);
struct iscsi_endpoint *beiscsi_ep_connect(struct Scsi_Host *shost,
struct sockaddr *dst_addr,
int non_blocking);
int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms);
void beiscsi_ep_disconnect(struct iscsi_endpoint *ep);
void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn,
struct iscsi_stats *stats);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,833 @@
/**
* Copyright (C) 2005 - 2009 ServerEngines
* 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 (jayamohank@serverengines.com)
*
* Contact Information:
* linux-drivers@serverengines.com
*
* ServerEngines
* 209 N. Fair Oaks Ave
* Sunnyvale, CA 94085
*
*/
#ifndef _BEISCSI_MAIN_
#define _BEISCSI_MAIN_
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/in.h>
#include <linux/blk-iopoll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/iscsi_proto.h>
#include <scsi/libiscsi.h>
#include <scsi/scsi_transport_iscsi.h>
#include "be.h"
#define DRV_NAME "be2iscsi"
#define BUILD_STR "2.0.527.0"
#define BE_NAME "ServerEngines BladeEngine2" \
"Linux iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
#define BE_VENDOR_ID 0x19A2
#define BE_DEVICE_ID1 0x212
#define OC_DEVICE_ID1 0x702
#define OC_DEVICE_ID2 0x703
#define BE2_MAX_SESSIONS 64
#define BE2_CMDS_PER_CXN 128
#define BE2_LOGOUTS BE2_MAX_SESSIONS
#define BE2_TMFS 16
#define BE2_NOPOUT_REQ 16
#define BE2_ASYNCPDUS BE2_MAX_SESSIONS
#define BE2_MAX_ICDS 2048
#define BE2_SGE 32
#define BE2_DEFPDU_HDR_SZ 64
#define BE2_DEFPDU_DATA_SZ 8192
#define BE2_IO_DEPTH \
(BE2_MAX_ICDS / 2 - (BE2_LOGOUTS + BE2_TMFS + BE2_NOPOUT_REQ))
#define BEISCSI_SGLIST_ELEMENTS BE2_SGE
#define BEISCSI_MAX_CMNDS 1024 /* Max IO's per Ctrlr sht->can_queue */
#define BEISCSI_CMD_PER_LUN 128 /* scsi_host->cmd_per_lun */
#define BEISCSI_MAX_SECTORS 2048 /* scsi_host->max_sectors */
#define BEISCSI_MAX_CMD_LEN 16 /* scsi_host->max_cmd_len */
#define BEISCSI_NUM_MAX_LUN 256 /* scsi_host->max_lun */
#define BEISCSI_NUM_DEVICES_SUPPORTED 0x01
#define BEISCSI_MAX_FRAGS_INIT 192
#define BE_NUM_MSIX_ENTRIES 1
#define MPU_EP_SEMAPHORE 0xac
#define BE_SENSE_INFO_SIZE 258
#define BE_ISCSI_PDU_HEADER_SIZE 64
#define BE_MIN_MEM_SIZE 16384
#define IIOC_SCSI_DATA 0x05 /* Write Operation */
#define DBG_LVL 0x00000001
#define DBG_LVL_1 0x00000001
#define DBG_LVL_2 0x00000002
#define DBG_LVL_3 0x00000004
#define DBG_LVL_4 0x00000008
#define DBG_LVL_5 0x00000010
#define DBG_LVL_6 0x00000020
#define DBG_LVL_7 0x00000040
#define DBG_LVL_8 0x00000080
#define SE_DEBUG(debug_mask, fmt, args...) \
do { \
if (debug_mask & DBG_LVL) { \
printk(KERN_ERR "(%s():%d):", __func__, __LINE__);\
printk(fmt, ##args); \
} \
} while (0);
/**
* hardware needs the async PDU buffers to be posted in multiples of 8
* So have atleast 8 of them by default
*/
#define HWI_GET_ASYNC_PDU_CTX(phwi) (phwi->phwi_ctxt->pasync_ctx)
/********* Memory BAR register ************/
#define PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET 0xfc
/**
* Host Interrupt Enable, if set interrupts are enabled although "PCI Interrupt
* Disable" may still globally block interrupts in addition to individual
* interrupt masks; a mechanism for the device driver to block all interrupts
* atomically without having to arbitrate for the PCI Interrupt Disable bit
* with the OS.
*/
#define MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK (1 << 29) /* bit 29 */
/********* ISR0 Register offset **********/
#define CEV_ISR0_OFFSET 0xC18
#define CEV_ISR_SIZE 4
/**
* Macros for reading/writing a protection domain or CSR registers
* in BladeEngine.
*/
#define DB_TXULP0_OFFSET 0x40
#define DB_RXULP0_OFFSET 0xA0
/********* Event Q door bell *************/
#define DB_EQ_OFFSET DB_CQ_OFFSET
#define DB_EQ_RING_ID_MASK 0x1FF /* bits 0 - 8 */
/* Clear the interrupt for this eq */
#define DB_EQ_CLR_SHIFT (9) /* bit 9 */
/* Must be 1 */
#define DB_EQ_EVNT_SHIFT (10) /* bit 10 */
/* Number of event entries processed */
#define DB_EQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */
/* Rearm bit */
#define DB_EQ_REARM_SHIFT (29) /* bit 29 */
/********* Compl Q door bell *************/
#define DB_CQ_OFFSET 0x120
#define DB_CQ_RING_ID_MASK 0x3FF /* bits 0 - 9 */
/* Number of event entries processed */
#define DB_CQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */
/* Rearm bit */
#define DB_CQ_REARM_SHIFT (29) /* bit 29 */
#define GET_HWI_CONTROLLER_WS(pc) (pc->phwi_ctrlr)
#define HWI_GET_DEF_BUFQ_ID(pc) (((struct hwi_controller *)\
(GET_HWI_CONTROLLER_WS(pc)))->default_pdu_data.id)
#define HWI_GET_DEF_HDRQ_ID(pc) (((struct hwi_controller *)\
(GET_HWI_CONTROLLER_WS(pc)))->default_pdu_hdr.id)
#define PAGES_REQUIRED(x) \
((x < PAGE_SIZE) ? 1 : ((x + PAGE_SIZE - 1) / PAGE_SIZE))
enum be_mem_enum {
HWI_MEM_ADDN_CONTEXT,
HWI_MEM_CQ,
HWI_MEM_EQ,
HWI_MEM_WRB,
HWI_MEM_WRBH,
HWI_MEM_SGLH, /* 5 */
HWI_MEM_SGE,
HWI_MEM_ASYNC_HEADER_BUF,
HWI_MEM_ASYNC_DATA_BUF,
HWI_MEM_ASYNC_HEADER_RING,
HWI_MEM_ASYNC_DATA_RING, /* 10 */
HWI_MEM_ASYNC_HEADER_HANDLE,
HWI_MEM_ASYNC_DATA_HANDLE,
HWI_MEM_ASYNC_PDU_CONTEXT,
ISCSI_MEM_GLOBAL_HEADER,
SE_MEM_MAX /* 15 */
};
struct be_bus_address32 {
unsigned int address_lo;
unsigned int address_hi;
};
struct be_bus_address64 {
unsigned long long address;
};
struct be_bus_address {
union {
struct be_bus_address32 a32;
struct be_bus_address64 a64;
} u;
};
struct mem_array {
struct be_bus_address bus_address; /* Bus address of location */
void *virtual_address; /* virtual address to the location */
unsigned int size; /* Size required by memory block */
};
struct be_mem_descriptor {
unsigned int index; /* Index of this memory parameter */
unsigned int category; /* type indicates cached/non-cached */
unsigned int num_elements; /* number of elements in this
* descriptor
*/
unsigned int alignment_mask; /* Alignment mask for this block */
unsigned int size_in_bytes; /* Size required by memory block */
struct mem_array *mem_array;
};
struct sgl_handle {
unsigned int sgl_index;
struct iscsi_sge *pfrag;
};
struct hba_parameters {
unsigned int ios_per_ctrl;
unsigned int cxns_per_ctrl;
unsigned int asyncpdus_per_ctrl;
unsigned int icds_per_ctrl;
unsigned int num_sge_per_io;
unsigned int defpdu_hdr_sz;
unsigned int defpdu_data_sz;
unsigned int num_cq_entries;
unsigned int num_eq_entries;
unsigned int wrbs_per_cxn;
unsigned int crashmode;
unsigned int hba_num;
unsigned int mgmt_ws_sz;
unsigned int hwi_ws_sz;
unsigned int eto;
unsigned int ldto;
unsigned int dbg_flags;
unsigned int num_cxn;
unsigned int eq_timer;
/**
* These are calculated from other params. They're here
* for debug purposes
*/
unsigned int num_mcc_pages;
unsigned int num_mcc_cq_pages;
unsigned int num_cq_pages;
unsigned int num_eq_pages;
unsigned int num_async_pdu_buf_pages;
unsigned int num_async_pdu_buf_sgl_pages;
unsigned int num_async_pdu_buf_cq_pages;
unsigned int num_async_pdu_hdr_pages;
unsigned int num_async_pdu_hdr_sgl_pages;
unsigned int num_async_pdu_hdr_cq_pages;
unsigned int num_sge;
};
struct beiscsi_hba {
struct hba_parameters params;
struct hwi_controller *phwi_ctrlr;
unsigned int mem_req[SE_MEM_MAX];
/* PCI BAR mapped addresses */
u8 __iomem *csr_va; /* CSR */
u8 __iomem *db_va; /* Door Bell */
u8 __iomem *pci_va; /* PCI Config */
struct be_bus_address csr_pa; /* CSR */
struct be_bus_address db_pa; /* CSR */
struct be_bus_address pci_pa; /* CSR */
/* PCI representation of our HBA */
struct pci_dev *pcidev;
unsigned int state;
unsigned short asic_revision;
struct blk_iopoll iopoll;
struct be_mem_descriptor *init_mem;
unsigned short io_sgl_alloc_index;
unsigned short io_sgl_free_index;
unsigned short io_sgl_hndl_avbl;
struct sgl_handle **io_sgl_hndl_base;
unsigned short eh_sgl_alloc_index;
unsigned short eh_sgl_free_index;
unsigned short eh_sgl_hndl_avbl;
struct sgl_handle **eh_sgl_hndl_base;
spinlock_t io_sgl_lock;
spinlock_t mgmt_sgl_lock;
spinlock_t isr_lock;
unsigned int age;
unsigned short avlbl_cids;
unsigned short cid_alloc;
unsigned short cid_free;
struct beiscsi_conn *conn_table[BE2_MAX_SESSIONS * 2];
struct list_head hba_queue;
unsigned short *cid_array;
struct iscsi_endpoint **ep_array;
struct Scsi_Host *shost;
struct {
/**
* group together since they are used most frequently
* for cid to cri conversion
*/
unsigned int iscsi_cid_start;
unsigned int phys_port;
unsigned int isr_offset;
unsigned int iscsi_icd_start;
unsigned int iscsi_cid_count;
unsigned int iscsi_icd_count;
unsigned int pci_function;
unsigned short cid_alloc;
unsigned short cid_free;
unsigned short avlbl_cids;
spinlock_t cid_lock;
} fw_config;
u8 mac_address[ETH_ALEN];
unsigned short todo_cq;
unsigned short todo_mcc_cq;
char wq_name[20];
struct workqueue_struct *wq; /* The actuak work queue */
struct work_struct work_cqs; /* The work being queued */
struct be_ctrl_info ctrl;
};
/**
* struct beiscsi_conn - iscsi connection structure
*/
struct beiscsi_conn {
struct iscsi_conn *conn;
struct beiscsi_hba *phba;
u32 exp_statsn;
u32 beiscsi_conn_cid;
struct beiscsi_endpoint *ep;
unsigned short login_in_progress;
struct sgl_handle *plogin_sgl_handle;
};
/* This structure is used by the chip */
struct pdu_data_out {
u32 dw[12];
};
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_pdu_data_out {
u8 opcode[6]; /* opcode */
u8 rsvd0[2]; /* should be 0 */
u8 rsvd1[7];
u8 final_bit; /* F bit */
u8 rsvd2[16];
u8 ahs_length[8]; /* no AHS */
u8 data_len_hi[8];
u8 data_len_lo[16]; /* DataSegmentLength */
u8 lun[64];
u8 itt[32]; /* ITT; initiator task tag */
u8 ttt[32]; /* TTT; valid for R2T or 0xffffffff */
u8 rsvd3[32];
u8 exp_stat_sn[32];
u8 rsvd4[32];
u8 data_sn[32];
u8 buffer_offset[32];
u8 rsvd5[32];
};
struct be_cmd_bhs {
struct iscsi_cmd iscsi_hdr;
unsigned char pad1[16];
struct pdu_data_out iscsi_data_pdu;
unsigned char pad2[BE_SENSE_INFO_SIZE -
sizeof(struct pdu_data_out)];
};
struct beiscsi_io_task {
struct wrb_handle *pwrb_handle;
struct sgl_handle *psgl_handle;
struct beiscsi_conn *conn;
struct scsi_cmnd *scsi_cmnd;
unsigned int cmd_sn;
unsigned int flags;
unsigned short cid;
unsigned short header_len;
unsigned int alloc_size;
struct be_cmd_bhs *cmd_bhs;
struct be_bus_address bhs_pa;
unsigned short bhs_len;
};
struct be_nonio_bhs {
struct iscsi_hdr iscsi_hdr;
unsigned char pad1[16];
struct pdu_data_out iscsi_data_pdu;
unsigned char pad2[BE_SENSE_INFO_SIZE -
sizeof(struct pdu_data_out)];
};
struct be_status_bhs {
struct iscsi_cmd iscsi_hdr;
unsigned char pad1[16];
/**
* The plus 2 below is to hold the sense info length that gets
* DMA'ed by RxULP
*/
unsigned char sense_info[BE_SENSE_INFO_SIZE];
};
struct iscsi_sge {
u32 dw[4];
};
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_iscsi_sge {
u8 addr_hi[32];
u8 addr_lo[32];
u8 sge_offset[22]; /* DWORD 2 */
u8 rsvd0[9]; /* DWORD 2 */
u8 last_sge; /* DWORD 2 */
u8 len[17]; /* DWORD 3 */
u8 rsvd1[15]; /* DWORD 3 */
};
struct beiscsi_offload_params {
u32 dw[5];
};
#define OFFLD_PARAMS_ERL 0x00000003
#define OFFLD_PARAMS_DDE 0x00000004
#define OFFLD_PARAMS_HDE 0x00000008
#define OFFLD_PARAMS_IR2T 0x00000010
#define OFFLD_PARAMS_IMD 0x00000020
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_beiscsi_offload_params {
u8 max_burst_length[32];
u8 max_send_data_segment_length[32];
u8 first_burst_length[32];
u8 erl[2];
u8 dde[1];
u8 hde[1];
u8 ir2t[1];
u8 imd[1];
u8 pad[26];
u8 exp_statsn[32];
};
/* void hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba, struct sol_cqe *psol);*/
struct async_pdu_handle {
struct list_head link;
struct be_bus_address pa;
void *pbuffer;
unsigned int consumed;
unsigned char index;
unsigned char is_header;
unsigned short cri;
unsigned long buffer_len;
};
struct hwi_async_entry {
struct {
unsigned char hdr_received;
unsigned char hdr_len;
unsigned short bytes_received;
unsigned int bytes_needed;
struct list_head list;
} wait_queue;
struct list_head header_busy_list;
struct list_head data_busy_list;
};
#define BE_MIN_ASYNC_ENTRIES 128
struct hwi_async_pdu_context {
struct {
struct be_bus_address pa_base;
void *va_base;
void *ring_base;
struct async_pdu_handle *handle_base;
unsigned int host_write_ptr;
unsigned int ep_read_ptr;
unsigned int writables;
unsigned int free_entries;
unsigned int busy_entries;
unsigned int buffer_size;
unsigned int num_entries;
struct list_head free_list;
} async_header;
struct {
struct be_bus_address pa_base;
void *va_base;
void *ring_base;
struct async_pdu_handle *handle_base;
unsigned int host_write_ptr;
unsigned int ep_read_ptr;
unsigned int writables;
unsigned int free_entries;
unsigned int busy_entries;
unsigned int buffer_size;
struct list_head free_list;
unsigned int num_entries;
} async_data;
/**
* This is a varying size list! Do not add anything
* after this entry!!
*/
struct hwi_async_entry async_entry[BE_MIN_ASYNC_ENTRIES];
};
#define PDUCQE_CODE_MASK 0x0000003F
#define PDUCQE_DPL_MASK 0xFFFF0000
#define PDUCQE_INDEX_MASK 0x0000FFFF
struct i_t_dpdu_cqe {
u32 dw[4];
} __packed;
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_i_t_dpdu_cqe {
u8 db_addr_hi[32];
u8 db_addr_lo[32];
u8 code[6];
u8 cid[10];
u8 dpl[16];
u8 index[16];
u8 num_cons[10];
u8 rsvd0[4];
u8 final;
u8 valid;
} __packed;
#define CQE_VALID_MASK 0x80000000
#define CQE_CODE_MASK 0x0000003F
#define CQE_CID_MASK 0x0000FFC0
#define EQE_VALID_MASK 0x00000001
#define EQE_MAJORCODE_MASK 0x0000000E
#define EQE_RESID_MASK 0xFFFF0000
struct be_eq_entry {
u32 dw[1];
} __packed;
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_eq_entry {
u8 valid; /* DWORD 0 */
u8 major_code[3]; /* DWORD 0 */
u8 minor_code[12]; /* DWORD 0 */
u8 resource_id[16]; /* DWORD 0 */
} __packed;
struct cq_db {
u32 dw[1];
} __packed;
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_cq_db {
u8 qid[10];
u8 event[1];
u8 rsvd0[5];
u8 num_popped[13];
u8 rearm[1];
u8 rsvd1[2];
} __packed;
void beiscsi_process_eq(struct beiscsi_hba *phba);
struct iscsi_wrb {
u32 dw[16];
} __packed;
#define WRB_TYPE_MASK 0xF0000000
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_iscsi_wrb {
u8 lun[14]; /* DWORD 0 */
u8 lt; /* DWORD 0 */
u8 invld; /* DWORD 0 */
u8 wrb_idx[8]; /* DWORD 0 */
u8 dsp; /* DWORD 0 */
u8 dmsg; /* DWORD 0 */
u8 undr_run; /* DWORD 0 */
u8 over_run; /* DWORD 0 */
u8 type[4]; /* DWORD 0 */
u8 ptr2nextwrb[8]; /* DWORD 1 */
u8 r2t_exp_dtl[24]; /* DWORD 1 */
u8 sgl_icd_idx[12]; /* DWORD 2 */
u8 rsvd0[20]; /* DWORD 2 */
u8 exp_data_sn[32]; /* DWORD 3 */
u8 iscsi_bhs_addr_hi[32]; /* DWORD 4 */
u8 iscsi_bhs_addr_lo[32]; /* DWORD 5 */
u8 cmdsn_itt[32]; /* DWORD 6 */
u8 dif_ref_tag[32]; /* DWORD 7 */
u8 sge0_addr_hi[32]; /* DWORD 8 */
u8 sge0_addr_lo[32]; /* DWORD 9 */
u8 sge0_offset[22]; /* DWORD 10 */
u8 pbs; /* DWORD 10 */
u8 dif_mode[2]; /* DWORD 10 */
u8 rsvd1[6]; /* DWORD 10 */
u8 sge0_last; /* DWORD 10 */
u8 sge0_len[17]; /* DWORD 11 */
u8 dif_meta_tag[14]; /* DWORD 11 */
u8 sge0_in_ddr; /* DWORD 11 */
u8 sge1_addr_hi[32]; /* DWORD 12 */
u8 sge1_addr_lo[32]; /* DWORD 13 */
u8 sge1_r2t_offset[22]; /* DWORD 14 */
u8 rsvd2[9]; /* DWORD 14 */
u8 sge1_last; /* DWORD 14 */
u8 sge1_len[17]; /* DWORD 15 */
u8 ref_sgl_icd_idx[12]; /* DWORD 15 */
u8 rsvd3[2]; /* DWORD 15 */
u8 sge1_in_ddr; /* DWORD 15 */
} __packed;
struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
int index);
void
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle);
struct pdu_nop_out {
u32 dw[12];
};
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_pdu_nop_out {
u8 opcode[6]; /* opcode 0x00 */
u8 i_bit; /* I Bit */
u8 x_bit; /* reserved; should be 0 */
u8 fp_bit_filler1[7];
u8 f_bit; /* always 1 */
u8 reserved1[16];
u8 ahs_length[8]; /* no AHS */
u8 data_len_hi[8];
u8 data_len_lo[16]; /* DataSegmentLength */
u8 lun[64];
u8 itt[32]; /* initiator id for ping or 0xffffffff */
u8 ttt[32]; /* target id for ping or 0xffffffff */
u8 cmd_sn[32];
u8 exp_stat_sn[32];
u8 reserved5[128];
};
#define PDUBASE_OPCODE_MASK 0x0000003F
#define PDUBASE_DATALENHI_MASK 0x0000FF00
#define PDUBASE_DATALENLO_MASK 0xFFFF0000
struct pdu_base {
u32 dw[16];
} __packed;
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_pdu_base {
u8 opcode[6];
u8 i_bit; /* immediate bit */
u8 x_bit; /* reserved, always 0 */
u8 reserved1[24]; /* opcode-specific fields */
u8 ahs_length[8]; /* length units is 4 byte words */
u8 data_len_hi[8];
u8 data_len_lo[16]; /* DatasegmentLength */
u8 lun[64]; /* lun or opcode-specific fields */
u8 itt[32]; /* initiator task tag */
u8 reserved4[224];
};
struct iscsi_target_context_update_wrb {
u32 dw[16];
} __packed;
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_iscsi_target_context_update_wrb {
u8 lun[14]; /* DWORD 0 */
u8 lt; /* DWORD 0 */
u8 invld; /* DWORD 0 */
u8 wrb_idx[8]; /* DWORD 0 */
u8 dsp; /* DWORD 0 */
u8 dmsg; /* DWORD 0 */
u8 undr_run; /* DWORD 0 */
u8 over_run; /* DWORD 0 */
u8 type[4]; /* DWORD 0 */
u8 ptr2nextwrb[8]; /* DWORD 1 */
u8 max_burst_length[19]; /* DWORD 1 */
u8 rsvd0[5]; /* DWORD 1 */
u8 rsvd1[15]; /* DWORD 2 */
u8 max_send_data_segment_length[17]; /* DWORD 2 */
u8 first_burst_length[14]; /* DWORD 3 */
u8 rsvd2[2]; /* DWORD 3 */
u8 tx_wrbindex_drv_msg[8]; /* DWORD 3 */
u8 rsvd3[5]; /* DWORD 3 */
u8 session_state[3]; /* DWORD 3 */
u8 rsvd4[16]; /* DWORD 4 */
u8 tx_jumbo; /* DWORD 4 */
u8 hde; /* DWORD 4 */
u8 dde; /* DWORD 4 */
u8 erl[2]; /* DWORD 4 */
u8 domain_id[5]; /* DWORD 4 */
u8 mode; /* DWORD 4 */
u8 imd; /* DWORD 4 */
u8 ir2t; /* DWORD 4 */
u8 notpredblq[2]; /* DWORD 4 */
u8 compltonack; /* DWORD 4 */
u8 stat_sn[32]; /* DWORD 5 */
u8 pad_buffer_addr_hi[32]; /* DWORD 6 */
u8 pad_buffer_addr_lo[32]; /* DWORD 7 */
u8 pad_addr_hi[32]; /* DWORD 8 */
u8 pad_addr_lo[32]; /* DWORD 9 */
u8 rsvd5[32]; /* DWORD 10 */
u8 rsvd6[32]; /* DWORD 11 */
u8 rsvd7[32]; /* DWORD 12 */
u8 rsvd8[32]; /* DWORD 13 */
u8 rsvd9[32]; /* DWORD 14 */
u8 rsvd10[32]; /* DWORD 15 */
} __packed;
struct be_ring {
u32 pages; /* queue size in pages */
u32 id; /* queue id assigned by beklib */
u32 num; /* number of elements in queue */
u32 cidx; /* consumer index */
u32 pidx; /* producer index -- not used by most rings */
u32 item_size; /* size in bytes of one object */
void *va; /* The virtual address of the ring. This
* should be last to allow 32 & 64 bit debugger
* extensions to work.
*/
};
struct hwi_wrb_context {
struct list_head wrb_handle_list;
struct list_head wrb_handle_drvr_list;
struct wrb_handle **pwrb_handle_base;
struct wrb_handle **pwrb_handle_basestd;
struct iscsi_wrb *plast_wrb;
unsigned short alloc_index;
unsigned short free_index;
unsigned short wrb_handles_available;
unsigned short cid;
};
struct hwi_controller {
struct list_head io_sgl_list;
struct list_head eh_sgl_list;
struct sgl_handle *psgl_handle_base;
unsigned int wrb_mem_index;
struct hwi_wrb_context wrb_context[BE2_MAX_SESSIONS * 2];
struct mcc_wrb *pmcc_wrb_base;
struct be_ring default_pdu_hdr;
struct be_ring default_pdu_data;
struct hwi_context_memory *phwi_ctxt;
unsigned short cq_errors[CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN];
};
enum hwh_type_enum {
HWH_TYPE_IO = 1,
HWH_TYPE_LOGOUT = 2,
HWH_TYPE_TMF = 3,
HWH_TYPE_NOP = 4,
HWH_TYPE_IO_RD = 5,
HWH_TYPE_LOGIN = 11,
HWH_TYPE_INVALID = 0xFFFFFFFF
};
struct wrb_handle {
enum hwh_type_enum type;
unsigned short wrb_index;
unsigned short nxt_wrb_index;
struct iscsi_task *pio_handle;
struct iscsi_wrb *pwrb;
};
struct hwi_context_memory {
struct be_eq_obj be_eq;
struct be_queue_info be_cq;
struct be_queue_info be_mcc_cq;
struct be_queue_info be_mcc;
struct be_queue_info be_def_hdrq;
struct be_queue_info be_def_dataq;
struct be_queue_info be_wrbq[BE2_MAX_SESSIONS];
struct be_mcc_wrb_context *pbe_mcc_context;
struct hwi_async_pdu_context *pasync_ctx;
};
#endif

View File

@ -0,0 +1,321 @@
/**
* Copyright (C) 2005 - 2009 ServerEngines
* 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 (jayamohank@serverengines.com)
*
* Contact Information:
* linux-drivers@serverengines.com
*
* ServerEngines
* 209 N. Fair Oaks Ave
* Sunnyvale, CA 94085
*
*/
#include "be_mgmt.h"
#include "be_iscsi.h"
unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
struct beiscsi_hba *phba)
{
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_fw_cfg *req = embedded_payload(wrb);
int status = 0;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req));
status = be_mbox_notify(ctrl);
if (!status) {
struct be_fw_cfg *pfw_cfg;
pfw_cfg = req;
phba->fw_config.phys_port = pfw_cfg->phys_port;
phba->fw_config.iscsi_icd_start =
pfw_cfg->ulp[0].icd_base;
phba->fw_config.iscsi_icd_count =
pfw_cfg->ulp[0].icd_count;
phba->fw_config.iscsi_cid_start =
pfw_cfg->ulp[0].sq_base;
phba->fw_config.iscsi_cid_count =
pfw_cfg->ulp[0].sq_count;
} else {
shost_printk(KERN_WARNING, phba->shost,
"Failed in mgmt_get_fw_config \n");
}
spin_unlock(&ctrl->mbox_lock);
return status;
}
unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
{
struct be_dma_mem nonemb_cmd;
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_mgmt_controller_attributes *req;
struct be_sge *sge = nonembedded_sgl(wrb);
int status = 0;
nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev,
sizeof(struct be_mgmt_controller_attributes),
&nonemb_cmd.dma);
if (nonemb_cmd.va == NULL) {
SE_DEBUG(DBG_LVL_1,
"Failed to allocate memory for mgmt_check_supported_fw"
"\n");
return -1;
}
nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes);
req = nonemb_cmd.va;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req));
sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma));
sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
sge->len = cpu_to_le32(nonemb_cmd.size);
status = be_mbox_notify(ctrl);
if (!status) {
struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va;
SE_DEBUG(DBG_LVL_8, "Firmware version of CMD: %s\n",
resp->params.hba_attribs.flashrom_version_string);
SE_DEBUG(DBG_LVL_8, "Firmware version is : %s\n",
resp->params.hba_attribs.firmware_version_string);
SE_DEBUG(DBG_LVL_8,
"Developer Build, not performing version check...\n");
} else
SE_DEBUG(DBG_LVL_1, " Failed in mgmt_check_supported_fw\n");
if (nonemb_cmd.va)
pci_free_consistent(ctrl->pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
spin_unlock(&ctrl->mbox_lock);
return status;
}
unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct iscsi_cleanup_req *req = embedded_payload(wrb);
int status = 0;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req));
req->chute = chute;
req->hdr_ring_id = 0;
req->data_ring_id = 0;
status = be_mbox_notify(ctrl);
if (status)
shost_printk(KERN_WARNING, phba->shost,
" mgmt_epfw_cleanup , FAILED\n");
spin_unlock(&ctrl->mbox_lock);
return status;
}
unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
unsigned int icd, unsigned int cid)
{
struct be_dma_mem nonemb_cmd;
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct be_sge *sge = nonembedded_sgl(wrb);
struct invalidate_commands_params_in *req;
int status = 0;
nonemb_cmd.va = pci_alloc_consistent(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 -1;
}
nonemb_cmd.size = sizeof(struct invalidate_commands_params_in);
req = nonemb_cmd.va;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS,
sizeof(*req));
req->ref_handle = 0;
req->cleanup_type = CMD_ISCSI_COMMAND_INVALIDATE;
req->icd_count = 0;
req->table[req->icd_count].icd = icd;
req->table[req->icd_count].cid = cid;
sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma));
sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
sge->len = cpu_to_le32(nonemb_cmd.size);
status = be_mbox_notify(ctrl);
if (status)
SE_DEBUG(DBG_LVL_1, "ICDS Invalidation Failed\n");
spin_unlock(&ctrl->mbox_lock);
if (nonemb_cmd.va)
pci_free_consistent(ctrl->pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return status;
}
unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
struct beiscsi_endpoint *beiscsi_ep,
unsigned short cid,
unsigned short issue_reset,
unsigned short savecfg_flag)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct iscsi_invalidate_connection_params_in *req =
embedded_payload(wrb);
int status = 0;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION,
sizeof(*req));
req->session_handle = beiscsi_ep->fw_handle;
req->cid = cid;
if (issue_reset)
req->cleanup_type = CMD_ISCSI_CONNECTION_ISSUE_TCP_RST;
else
req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE;
req->save_cfg = savecfg_flag;
status = be_mbox_notify(ctrl);
if (status)
SE_DEBUG(DBG_LVL_1, "Invalidation Failed\n");
spin_unlock(&ctrl->mbox_lock);
return status;
}
unsigned char mgmt_upload_connection(struct beiscsi_hba *phba,
unsigned short cid, unsigned int upload_flag)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct tcp_upload_params_in *req = embedded_payload(wrb);
int status = 0;
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_COMMON_TCP_UPLOAD,
OPCODE_COMMON_TCP_UPLOAD, sizeof(*req));
req->id = (unsigned short)cid;
req->upload_type = (unsigned char)upload_flag;
status = be_mbox_notify(ctrl);
if (status)
SE_DEBUG(DBG_LVL_1, "mgmt_upload_connection Failed\n");
spin_unlock(&ctrl->mbox_lock);
return status;
}
int mgmt_open_connection(struct beiscsi_hba *phba,
struct sockaddr *dst_addr,
struct beiscsi_endpoint *beiscsi_ep)
{
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr;
struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr;
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct tcp_connect_and_offload_in *req = embedded_payload(wrb);
unsigned short def_hdr_id;
unsigned short def_data_id;
struct phys_addr template_address = { 0, 0 };
struct phys_addr *ptemplate_address;
int status = 0;
unsigned short cid = beiscsi_ep->ep_cid;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
def_hdr_id = (unsigned short)HWI_GET_DEF_HDRQ_ID(phba);
def_data_id = (unsigned short)HWI_GET_DEF_BUFQ_ID(phba);
ptemplate_address = &template_address;
ISCSI_GET_PDU_TEMPLATE_ADDRESS(phba, ptemplate_address);
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD,
sizeof(*req));
if (dst_addr->sa_family == PF_INET) {
__be32 s_addr = daddr_in->sin_addr.s_addr;
req->ip_address.ip_type = BE2_IPV4;
req->ip_address.ip_address[0] = s_addr & 0x000000ff;
req->ip_address.ip_address[1] = (s_addr & 0x0000ff00) >> 8;
req->ip_address.ip_address[2] = (s_addr & 0x00ff0000) >> 16;
req->ip_address.ip_address[3] = (s_addr & 0xff000000) >> 24;
req->tcp_port = ntohs(daddr_in->sin_port);
beiscsi_ep->dst_addr = daddr_in->sin_addr.s_addr;
beiscsi_ep->dst_tcpport = ntohs(daddr_in->sin_port);
beiscsi_ep->ip_type = BE2_IPV4;
} else if (dst_addr->sa_family == PF_INET6) {
req->ip_address.ip_type = BE2_IPV6;
memcpy(&req->ip_address.ip_address,
&daddr_in6->sin6_addr.in6_u.u6_addr8, 16);
req->tcp_port = ntohs(daddr_in6->sin6_port);
beiscsi_ep->dst_tcpport = ntohs(daddr_in6->sin6_port);
memcpy(&beiscsi_ep->dst6_addr,
&daddr_in6->sin6_addr.in6_u.u6_addr8, 16);
beiscsi_ep->ip_type = BE2_IPV6;
} else{
shost_printk(KERN_ERR, phba->shost, "unknown addr family %d\n",
dst_addr->sa_family);
spin_unlock(&ctrl->mbox_lock);
return -EINVAL;
}
req->cid = cid;
req->cq_id = phwi_context->be_cq.id;
req->defq_id = def_hdr_id;
req->hdr_ring_id = def_hdr_id;
req->data_ring_id = def_data_id;
req->do_offload = 1;
req->dataout_template_pa.lo = ptemplate_address->lo;
req->dataout_template_pa.hi = ptemplate_address->hi;
status = be_mbox_notify(ctrl);
if (!status) {
struct iscsi_endpoint *ep;
struct tcp_connect_and_offload_out *ptcpcnct_out =
embedded_payload(wrb);
ep = phba->ep_array[ptcpcnct_out->cid];
beiscsi_ep = ep->dd_data;
beiscsi_ep->fw_handle = 0;
beiscsi_ep->cid_vld = 1;
SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
} else
SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed\n");
spin_unlock(&ctrl->mbox_lock);
return status;
}

View File

@ -0,0 +1,249 @@
/**
* Copyright (C) 2005 - 2009 ServerEngines
* 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 (jayamohank@serverengines.com)
*
* Contact Information:
* linux-drivers@serverengines.com
*
* ServerEngines
* 209 N. Fair Oaks Ave
* Sunnyvale, CA 94085
*
*/
#ifndef _BEISCSI_MGMT_
#define _BEISCSI_MGMT_
#include <linux/types.h>
#include <linux/list.h>
#include "be_iscsi.h"
#include "be_main.h"
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_mcc_sge {
u8 pa_lo[32]; /* dword 0 */
u8 pa_hi[32]; /* dword 1 */
u8 length[32]; /* DWORD 2 */
} __packed;
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_mcc_wrb_payload {
union {
struct amap_mcc_sge sgl[19];
u8 embedded[59 * 32]; /* DWORDS 57 to 115 */
} u;
} __packed;
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
struct amap_mcc_wrb {
u8 embedded; /* DWORD 0 */
u8 rsvd0[2]; /* DWORD 0 */
u8 sge_count[5]; /* DWORD 0 */
u8 rsvd1[16]; /* DWORD 0 */
u8 special[8]; /* DWORD 0 */
u8 payload_length[32];
u8 tag[64]; /* DWORD 2 */
u8 rsvd2[32]; /* DWORD 4 */
struct amap_mcc_wrb_payload payload;
};
struct mcc_sge {
u32 pa_lo; /* dword 0 */
u32 pa_hi; /* dword 1 */
u32 length; /* DWORD 2 */
} __packed;
struct mcc_wrb_payload {
union {
struct mcc_sge sgl[19];
u32 embedded[59]; /* DWORDS 57 to 115 */
} u;
} __packed;
#define MCC_WRB_EMBEDDED_MASK 0x00000001
struct mcc_wrb {
u32 dw[0]; /* DWORD 0 */
u32 payload_length;
u32 tag[2]; /* DWORD 2 */
u32 rsvd2[1]; /* DWORD 4 */
struct mcc_wrb_payload payload;
};
unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute);
int mgmt_open_connection(struct beiscsi_hba *phba, struct sockaddr *dst_addr,
struct beiscsi_endpoint *beiscsi_ep);
unsigned char mgmt_upload_connection(struct beiscsi_hba *phba,
unsigned short cid,
unsigned int upload_flag);
unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
unsigned int icd, unsigned int cid);
struct iscsi_invalidate_connection_params_in {
struct be_cmd_req_hdr hdr;
unsigned int session_handle;
unsigned short cid;
unsigned short unused;
unsigned short cleanup_type;
unsigned short save_cfg;
} __packed;
struct iscsi_invalidate_connection_params_out {
unsigned int session_handle;
unsigned short cid;
unsigned short unused;
} __packed;
union iscsi_invalidate_connection_params {
struct iscsi_invalidate_connection_params_in request;
struct iscsi_invalidate_connection_params_out response;
} __packed;
struct invalidate_command_table {
unsigned short icd;
unsigned short cid;
} __packed;
struct invalidate_commands_params_in {
struct be_cmd_req_hdr hdr;
unsigned int ref_handle;
unsigned int icd_count;
struct invalidate_command_table table[128];
unsigned short cleanup_type;
unsigned short unused;
} __packed;
struct invalidate_commands_params_out {
unsigned int ref_handle;
unsigned int icd_count;
unsigned int icd_status[128];
} __packed;
union invalidate_commands_params {
struct invalidate_commands_params_in request;
struct invalidate_commands_params_out response;
} __packed;
struct mgmt_hba_attributes {
u8 flashrom_version_string[32];
u8 manufacturer_name[32];
u32 supported_modes;
u8 seeprom_version_lo;
u8 seeprom_version_hi;
u8 rsvd0[2];
u32 fw_cmd_data_struct_version;
u32 ep_fw_data_struct_version;
u32 future_reserved[12];
u32 default_extended_timeout;
u8 controller_model_number[32];
u8 controller_description[64];
u8 controller_serial_number[32];
u8 ip_version_string[32];
u8 firmware_version_string[32];
u8 bios_version_string[32];
u8 redboot_version_string[32];
u8 driver_version_string[32];
u8 fw_on_flash_version_string[32];
u32 functionalities_supported;
u16 max_cdblength;
u8 asic_revision;
u8 generational_guid[16];
u8 hba_port_count;
u16 default_link_down_timeout;
u8 iscsi_ver_min_max;
u8 multifunction_device;
u8 cache_valid;
u8 hba_status;
u8 max_domains_supported;
u8 phy_port;
u32 firmware_post_status;
u32 hba_mtu[8];
u32 future_u32[4];
} __packed;
struct mgmt_controller_attributes {
struct mgmt_hba_attributes hba_attribs;
u16 pci_vendor_id;
u16 pci_device_id;
u16 pci_sub_vendor_id;
u16 pci_sub_system_id;
u8 pci_bus_number;
u8 pci_device_number;
u8 pci_function_number;
u8 interface_type;
u64 unique_identifier;
u8 netfilters;
u8 rsvd0[3];
u8 future_u32[4];
} __packed;
struct be_mgmt_controller_attributes {
struct be_cmd_req_hdr hdr;
struct mgmt_controller_attributes params;
} __packed;
struct be_mgmt_controller_attributes_resp {
struct be_cmd_resp_hdr hdr;
struct mgmt_controller_attributes params;
} __packed;
/* configuration management */
#define GET_MGMT_CONTROLLER_WS(phba) (phba->pmgmt_ws)
/* MGMT CMD flags */
#define MGMT_CMDH_FREE (1<<0)
/* --- MGMT_ERROR_CODES --- */
/* Error Codes returned in the status field of the CMD response header */
#define MGMT_STATUS_SUCCESS 0 /* The CMD completed without errors */
#define MGMT_STATUS_FAILED 1 /* Error status in the Status field of */
/* the CMD_RESPONSE_HEADER */
#define ISCSI_GET_PDU_TEMPLATE_ADDRESS(pc, pa) {\
pa->lo = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\
bus_address.u.a32.address_lo; \
pa->hi = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\
bus_address.u.a32.address_hi; \
}
struct beiscsi_endpoint {
struct beiscsi_hba *phba;
struct beiscsi_sess *sess;
struct beiscsi_conn *conn;
unsigned short ip_type;
char dst6_addr[ISCSI_ADDRESS_BUF_LEN];
unsigned long dst_addr;
unsigned short ep_cid;
unsigned int fw_handle;
u16 dst_tcpport;
u16 cid_vld;
};
unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
struct beiscsi_hba *phba);
unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
struct beiscsi_endpoint *beiscsi_ep,
unsigned short cid,
unsigned short issue_reset,
unsigned short savecfg_flag);
#endif