Commit 938f372c authored by Jitendra Bhivare's avatar Jitendra Bhivare Committed by Martin K. Petersen

scsi: be2iscsi: Fix async PDU handling path

BUG: unable to handle kernel NULL pointer dereference at 000000000000015e
IP: [<ffffffffa0081700>]
hwi_get_async_handle.isra.23.constprop.39+0x90/0x1d0 [be2iscsi]
PGD 0
Oops: 0000 [#1] SMP
...
Call Trace:
 <IRQ>
 [<ffffffffa00818bc>] hwi_process_default_pdu_ring+0x7c/0x280 [be2iscsi]
 [<ffffffffa0088f51>] beiscsi_process_cq+0x321/0xb90 [be2iscsi]
 [<ffffffff810af028>] ? __wake_up_common+0x58/0x90
 [<ffffffff810b0d84>] ? __wake_up+0x44/0x50
 [<ffffffffa0089a2d>] be_iopoll+0x1d/0xb0 [be2iscsi]
 [<ffffffff812d1f61>] blk_iopoll_softirq+0xc1/0x100
 [<ffffffff81084b0f>] __do_softirq+0xef/0x280

The symptom observed is multiple async handles get queued for same index
thus causing leak in buffers posted to FW.

The root cause is:
- async handle is continued to be used even if it does not match the
completion.
- list_move operation done on already filled index.

1. Remove use of writables, host_write_ptr and ep_read_ptr.
2. Remove consumed logic to update writables. Instead, use only
free_entries to do the accounting of handles to be posted back.
3. Remove busy_list, instead use simple slot to index handles.
4. Added check no data, header less and overflow to make sure
all async_handles are flushed in error cases.
5. Added code to verify gathering of handles to form PDU by
checking final bit before forwarding PDU.
6. Added code to catch mismatch with CQE and handle gracefully.
7. Use AMAP, traverse cri_wait_queue list to post buffers, log
"async PDU" related errors.
8. Rearranged few data structures and added comments in init &
processing path.
9. Added WARN_ONs to catch any HD ring corruption.
Signed-off-by: default avatarJitendra Bhivare <jitendra.bhivare@broadcom.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 4ee1ec42
...@@ -897,57 +897,6 @@ void hwi_ring_cq_db(struct beiscsi_hba *phba, ...@@ -897,57 +897,6 @@ void hwi_ring_cq_db(struct beiscsi_hba *phba,
iowrite32(val, phba->db_va + DB_CQ_OFFSET); iowrite32(val, phba->db_va + DB_CQ_OFFSET);
} }
static unsigned int
beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba,
struct pdu_base *ppdu,
unsigned long pdu_len,
void *pbuffer, unsigned long buf_len)
{
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
struct iscsi_task *task;
struct beiscsi_io_task *io_task;
struct iscsi_hdr *login_hdr;
switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] &
PDUBASE_OPCODE_MASK) {
case ISCSI_OP_NOOP_IN:
pbuffer = NULL;
buf_len = 0;
break;
case ISCSI_OP_ASYNC_EVENT:
break;
case ISCSI_OP_REJECT:
WARN_ON(!pbuffer);
WARN_ON(!(buf_len == 48));
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
"BM_%d : In ISCSI_OP_REJECT\n");
break;
case ISCSI_OP_LOGIN_RSP:
case ISCSI_OP_TEXT_RSP:
task = conn->login_task;
io_task = task->dd_data;
login_hdr = (struct iscsi_hdr *)ppdu;
login_hdr->itt = io_task->libiscsi_itt;
break;
default:
beiscsi_log(phba, KERN_WARNING,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
"BM_%d : Unrecognized opcode 0x%x in async msg\n",
(ppdu->
dw[offsetof(struct amap_pdu_base, opcode) / 32]
& PDUBASE_OPCODE_MASK));
return 1;
}
spin_lock_bh(&session->back_lock);
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)ppdu, pbuffer, buf_len);
spin_unlock_bh(&session->back_lock);
return 0;
}
static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba) static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
{ {
struct sgl_handle *psgl_handle; struct sgl_handle *psgl_handle;
...@@ -1434,431 +1383,428 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn, ...@@ -1434,431 +1383,428 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
spin_unlock_bh(&session->back_lock); spin_unlock_bh(&session->back_lock);
} }
static struct list_head *hwi_get_async_busy_list(struct hwi_async_pdu_context /**
*pasync_ctx, unsigned int is_header, * ASYNC PDUs include
unsigned int host_write_ptr) * a. Unsolicited NOP-In (target initiated NOP-In)
* b. ASYNC Messages
* c. Reject PDU
* d. Login response
* These headers arrive unprocessed by the EP firmware.
* iSCSI layer processes them.
*/
static unsigned int
beiscsi_complete_pdu(struct beiscsi_conn *beiscsi_conn,
struct pdu_base *phdr, void *pdata, unsigned int dlen)
{ {
if (is_header) struct beiscsi_hba *phba = beiscsi_conn->phba;
return &pasync_ctx->async_entry[host_write_ptr]. struct iscsi_conn *conn = beiscsi_conn->conn;
header_busy_list; struct beiscsi_io_task *io_task;
else struct iscsi_hdr *login_hdr;
return &pasync_ctx->async_entry[host_write_ptr].data_busy_list; struct iscsi_task *task;
u8 code;
code = AMAP_GET_BITS(struct amap_pdu_base, opcode, phdr);
switch (code) {
case ISCSI_OP_NOOP_IN:
pdata = NULL;
dlen = 0;
break;
case ISCSI_OP_ASYNC_EVENT:
break;
case ISCSI_OP_REJECT:
WARN_ON(!pdata);
WARN_ON(!(dlen == 48));
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
"BM_%d : In ISCSI_OP_REJECT\n");
break;
case ISCSI_OP_LOGIN_RSP:
case ISCSI_OP_TEXT_RSP:
task = conn->login_task;
io_task = task->dd_data;
login_hdr = (struct iscsi_hdr *)phdr;
login_hdr->itt = io_task->libiscsi_itt;
break;
default:
beiscsi_log(phba, KERN_WARNING,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
"BM_%d : unrecognized async PDU opcode 0x%x\n",
code);
return 1;
}
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)phdr, pdata, dlen);
return 0;
}
static inline void
beiscsi_hdl_put_handle(struct hd_async_context *pasync_ctx,
struct hd_async_handle *pasync_handle)
{
if (pasync_handle->is_header) {
list_add_tail(&pasync_handle->link,
&pasync_ctx->async_header.free_list);
pasync_ctx->async_header.free_entries++;
} else {
list_add_tail(&pasync_handle->link,
&pasync_ctx->async_data.free_list);
pasync_ctx->async_data.free_entries++;
}
} }
static struct async_pdu_handle * static struct hd_async_handle *
hwi_get_async_handle(struct beiscsi_hba *phba, beiscsi_hdl_get_handle(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_conn *beiscsi_conn, struct hd_async_context *pasync_ctx,
struct hwi_async_pdu_context *pasync_ctx, struct i_t_dpdu_cqe *pdpdu_cqe)
struct i_t_dpdu_cqe *pdpdu_cqe, unsigned int *pcq_index)
{ {
struct beiscsi_hba *phba = beiscsi_conn->phba;
struct hd_async_handle *pasync_handle;
struct be_bus_address phys_addr; struct be_bus_address phys_addr;
struct list_head *pbusy_list; u8 final, error = 0;
struct async_pdu_handle *pasync_handle = NULL; u16 cid, code, ci;
unsigned char is_header = 0; u32 dpl;
unsigned int index, dpl;
cid = beiscsi_conn->beiscsi_conn_cid;
/**
* This function is invoked to get the right async_handle structure
* from a given DEF PDU CQ entry.
*
* - index in CQ entry gives the vertical index
* - address in CQ entry is the offset where the DMA last ended
* - final - no more notifications for this PDU
*/
if (is_chip_be2_be3r(phba)) { if (is_chip_be2_be3r(phba)) {
dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
dpl, pdpdu_cqe); dpl, pdpdu_cqe);
index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
index, pdpdu_cqe); index, pdpdu_cqe);
final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
final, pdpdu_cqe);
} else { } else {
dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
dpl, pdpdu_cqe); dpl, pdpdu_cqe);
index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
index, pdpdu_cqe); index, pdpdu_cqe);
final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
final, pdpdu_cqe);
} }
phys_addr.u.a32.address_lo = /**
(pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, * DB addr Hi/Lo is same for BE and SKH.
db_addr_lo) / 32] - dpl); * Subtract the dataplacementlength to get to the base.
phys_addr.u.a32.address_hi = */
pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, phys_addr.u.a32.address_lo = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
db_addr_hi) / 32]; db_addr_lo, pdpdu_cqe);
phys_addr.u.a32.address_lo -= dpl;
phys_addr.u.a64.address = phys_addr.u.a32.address_hi = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
*((unsigned long long *)(&phys_addr.u.a64.address)); db_addr_hi, pdpdu_cqe);
switch (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, code) / 32] code = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, code, pdpdu_cqe);
& PDUCQE_CODE_MASK) { switch (code) {
case UNSOL_HDR_NOTIFY: case UNSOL_HDR_NOTIFY:
is_header = 1; pasync_handle = pasync_ctx->async_entry[ci].header;
pbusy_list = hwi_get_async_busy_list(pasync_ctx,
is_header, index);
break; break;
case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
error = 1;
case UNSOL_DATA_NOTIFY: case UNSOL_DATA_NOTIFY:
pbusy_list = hwi_get_async_busy_list(pasync_ctx, pasync_handle = pasync_ctx->async_entry[ci].data;
is_header, index);
break; break;
/* called only for above codes */
default: default:
pbusy_list = NULL; pasync_handle = NULL;
beiscsi_log(phba, KERN_WARNING, break;
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
"BM_%d : Unexpected code=%d\n",
pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
code) / 32] & PDUCQE_CODE_MASK);
return NULL;
} }
WARN_ON(list_empty(pbusy_list)); if (!pasync_handle) {
list_for_each_entry(pasync_handle, pbusy_list, link) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
if (pasync_handle->pa.u.a64.address == phys_addr.u.a64.address) "BM_%d : cid %d async PDU handle not found - code %d ci %d addr %llx\n",
break; cid, code, ci, phys_addr.u.a64.address);
return pasync_handle;
} }
WARN_ON(!pasync_handle); if (pasync_handle->pa.u.a64.address != phys_addr.u.a64.address ||
pasync_handle->index != ci) {
/* driver bug - if ci does not match async handle index */
error = 1;
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
"BM_%d : cid %u async PDU handle mismatch - addr in %cQE %llx at %u:addr in CQE %llx ci %u\n",
cid, pasync_handle->is_header ? 'H' : 'D',
pasync_handle->pa.u.a64.address,
pasync_handle->index,
phys_addr.u.a64.address, ci);
/* FW has stale address - attempt continuing by dropping */
}
pasync_handle->cri = BE_GET_ASYNC_CRI_FROM_CID( /**
beiscsi_conn->beiscsi_conn_cid); * Each CID is associated with unique CRI.
pasync_handle->is_header = is_header; * ASYNC_CRI_FROM_CID mapping and CRI_FROM_CID are totaly different.
**/
pasync_handle->cri = BE_GET_ASYNC_CRI_FROM_CID(cid);
pasync_handle->is_final = final;
pasync_handle->buffer_len = dpl; pasync_handle->buffer_len = dpl;
*pcq_index = index; /* empty the slot */
if (pasync_handle->is_header)
pasync_ctx->async_entry[ci].header = NULL;
else
pasync_ctx->async_entry[ci].data = NULL;
/**
* DEF PDU header and data buffers with errors should be simply
* dropped as there are no consumers for it.
*/
if (error) {
beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
pasync_handle = NULL;
}
return pasync_handle; return pasync_handle;
} }
static unsigned int static void
hwi_update_async_writables(struct beiscsi_hba *phba, beiscsi_hdl_purge_handles(struct beiscsi_hba *phba,
struct hwi_async_pdu_context *pasync_ctx, struct hd_async_context *pasync_ctx,
unsigned int is_header, unsigned int cq_index) u16 cri)
{ {
struct list_head *pbusy_list; struct hd_async_handle *pasync_handle, *tmp_handle;
struct async_pdu_handle *pasync_handle; struct list_head *plist;
unsigned int num_entries, writables = 0;
unsigned int *pep_read_ptr, *pwritables;
num_entries = pasync_ctx->num_entries; plist = &pasync_ctx->async_entry[cri].wq.list;
if (is_header) { list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) {
pep_read_ptr = &pasync_ctx->async_header.ep_read_ptr; list_del(&pasync_handle->link);
pwritables = &pasync_ctx->async_header.writables; beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
} else {
pep_read_ptr = &pasync_ctx->async_data.ep_read_ptr;
pwritables = &pasync_ctx->async_data.writables;
} }
while ((*pep_read_ptr) != cq_index) { INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wq.list);
(*pep_read_ptr)++; pasync_ctx->async_entry[cri].wq.hdr_len = 0;
*pep_read_ptr = (*pep_read_ptr) % num_entries; pasync_ctx->async_entry[cri].wq.bytes_received = 0;
pasync_ctx->async_entry[cri].wq.bytes_needed = 0;
}
pbusy_list = hwi_get_async_busy_list(pasync_ctx, is_header, static unsigned int
*pep_read_ptr); beiscsi_hdl_fwd_pdu(struct beiscsi_conn *beiscsi_conn,
if (writables == 0) struct hd_async_context *pasync_ctx,
WARN_ON(list_empty(pbusy_list)); u16 cri)
{
struct iscsi_session *session = beiscsi_conn->conn->session;
struct hd_async_handle *pasync_handle, *plast_handle;
struct beiscsi_hba *phba = beiscsi_conn->phba;
void *phdr = NULL, *pdata = NULL;
u32 dlen = 0, status = 0;
struct list_head *plist;
if (!list_empty(pbusy_list)) { plist = &pasync_ctx->async_entry[cri].wq.list;
pasync_handle = list_entry(pbusy_list->next, plast_handle = NULL;
struct async_pdu_handle, list_for_each_entry(pasync_handle, plist, link) {
link); plast_handle = pasync_handle;
WARN_ON(!pasync_handle); /* get the header, the first entry */
pasync_handle->consumed = 1; if (!phdr) {
phdr = pasync_handle->pbuffer;
continue;
} }
/* use first buffer to collect all the data */
writables++; if (!pdata) {
pdata = pasync_handle->pbuffer;
dlen = pasync_handle->buffer_len;
continue;
} }
memcpy(pdata + dlen, pasync_handle->pbuffer,
if (!writables) { pasync_handle->buffer_len);
beiscsi_log(phba, KERN_ERR, dlen += pasync_handle->buffer_len;
BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
"BM_%d : Duplicate notification received - index 0x%x!!\n",
cq_index);
WARN_ON(1);
} }
*pwritables = *pwritables + writables; if (!plast_handle->is_final) {
return 0; /* last handle should have final PDU notification from FW */
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
"BM_%d : cid %u %p fwd async PDU with last handle missing - HL%u:DN%u:DR%u\n",
beiscsi_conn->beiscsi_conn_cid, plast_handle,
pasync_ctx->async_entry[cri].wq.hdr_len,
pasync_ctx->async_entry[cri].wq.bytes_needed,
pasync_ctx->async_entry[cri].wq.bytes_received);
}
spin_lock_bh(&session->back_lock);
status = beiscsi_complete_pdu(beiscsi_conn, phdr, pdata, dlen);
spin_unlock_bh(&session->back_lock);
beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
return status;
} }
static void hwi_free_async_msg(struct beiscsi_hba *phba, static unsigned int
struct hwi_async_pdu_context *pasync_ctx, beiscsi_hdl_gather_pdu(struct beiscsi_conn *beiscsi_conn,
unsigned int cri) struct hd_async_context *pasync_ctx,
struct hd_async_handle *pasync_handle)
{ {
struct async_pdu_handle *pasync_handle, *tmp_handle; unsigned int bytes_needed = 0, status = 0;
struct list_head *plist; u16 cri = pasync_handle->cri;
struct cri_wait_queue *wq;
plist = &pasync_ctx->async_entry[cri].wait_queue.list; struct beiscsi_hba *phba;
list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) { struct pdu_base *ppdu;
list_del(&pasync_handle->link); char *err = "";
phba = beiscsi_conn->phba;
wq = &pasync_ctx->async_entry[cri].wq;
if (pasync_handle->is_header) { if (pasync_handle->is_header) {
list_add_tail(&pasync_handle->link, /* check if PDU hdr is rcv'd when old hdr not completed */
&pasync_ctx->async_header.free_list); if (wq->hdr_len) {
pasync_ctx->async_header.free_entries++; err = "incomplete";
} else { goto drop_pdu;
list_add_tail(&pasync_handle->link,
&pasync_ctx->async_data.free_list);
pasync_ctx->async_data.free_entries++;
} }
ppdu = pasync_handle->pbuffer;
bytes_needed = AMAP_GET_BITS(struct amap_pdu_base,
data_len_hi, ppdu);
bytes_needed <<= 16;
bytes_needed |= be16_to_cpu(AMAP_GET_BITS(struct amap_pdu_base,
data_len_lo, ppdu));
wq->hdr_len = pasync_handle->buffer_len;
wq->bytes_received = 0;
wq->bytes_needed = bytes_needed;
list_add_tail(&pasync_handle->link, &wq->list);
if (!bytes_needed)
status = beiscsi_hdl_fwd_pdu(beiscsi_conn,
pasync_ctx, cri);
} else {
/* check if data received has header and is needed */
if (!wq->hdr_len || !wq->bytes_needed) {
err = "header less";
goto drop_pdu;
}
wq->bytes_received += pasync_handle->buffer_len;
/* Something got overwritten? Better catch it here. */
if (wq->bytes_received > wq->bytes_needed) {
err = "overflow";
goto drop_pdu;
}
list_add_tail(&pasync_handle->link, &wq->list);
if (wq->bytes_received == wq->bytes_needed)
status = beiscsi_hdl_fwd_pdu(beiscsi_conn,
pasync_ctx, cri);
} }
return status;
INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wait_queue.list); drop_pdu:
pasync_ctx->async_entry[cri].wait_queue.hdr_received = 0; beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; "BM_%d : cid %u async PDU %s - def-%c:HL%u:DN%u:DR%u\n",
} beiscsi_conn->beiscsi_conn_cid, err,
pasync_handle->is_header ? 'H' : 'D',
static struct phys_addr * wq->hdr_len, wq->bytes_needed,
hwi_get_ring_address(struct hwi_async_pdu_context *pasync_ctx, pasync_handle->buffer_len);
unsigned int is_header, unsigned int host_write_ptr) /* discard this handle */
{ beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
struct phys_addr *pasync_sge = NULL; /* free all the other handles in cri_wait_queue */
beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
if (is_header) /* try continuing */
pasync_sge = pasync_ctx->async_header.ring_base; return status;
else
pasync_sge = pasync_ctx->async_data.ring_base;
return pasync_sge + host_write_ptr;
} }
static void hwi_post_async_buffers(struct beiscsi_hba *phba, static void
unsigned int is_header, uint8_t ulp_num) beiscsi_hdq_post_handles(struct beiscsi_hba *phba,
u8 header, u8 ulp_num)
{ {
struct hd_async_handle *pasync_handle, *tmp, **slot;
struct hd_async_context *pasync_ctx;
struct hwi_controller *phwi_ctrlr; struct hwi_controller *phwi_ctrlr;
struct hwi_async_pdu_context *pasync_ctx; struct list_head *hfree_list;
struct async_pdu_handle *pasync_handle;
struct list_head *pfree_link, *pbusy_list;
struct phys_addr *pasync_sge; struct phys_addr *pasync_sge;
unsigned int ring_id, num_entries; u32 ring_id, doorbell = 0;
unsigned int host_write_num, doorbell_offset; u16 index, num_entries;
unsigned int writables; u32 doorbell_offset;
unsigned int i = 0; u16 prod = 0, cons;
u32 doorbell = 0;
phwi_ctrlr = phba->phwi_ctrlr; phwi_ctrlr = phba->phwi_ctrlr;
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num); pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num);
num_entries = pasync_ctx->num_entries; num_entries = pasync_ctx->num_entries;
if (header) {
if (is_header) { cons = pasync_ctx->async_header.free_entries;
writables = min(pasync_ctx->async_header.writables, hfree_list = &pasync_ctx->async_header.free_list;
pasync_ctx->async_header.free_entries);
pfree_link = pasync_ctx->async_header.free_list.next;
host_write_num = pasync_ctx->async_header.host_write_ptr;
ring_id = phwi_ctrlr->default_pdu_hdr[ulp_num].id; ring_id = phwi_ctrlr->default_pdu_hdr[ulp_num].id;
doorbell_offset = phwi_ctrlr->default_pdu_hdr[ulp_num]. doorbell_offset = phwi_ctrlr->default_pdu_hdr[ulp_num].
doorbell_offset; doorbell_offset;
} else { } else {
writables = min(pasync_ctx->async_data.writables, cons = pasync_ctx->async_data.free_entries;
pasync_ctx->async_data.free_entries); hfree_list = &pasync_ctx->async_data.free_list;
pfree_link = pasync_ctx->async_data.free_list.next;
host_write_num = pasync_ctx->async_data.host_write_ptr;
ring_id = phwi_ctrlr->default_pdu_data[ulp_num].id; ring_id = phwi_ctrlr->default_pdu_data[ulp_num].id;
doorbell_offset = phwi_ctrlr->default_pdu_data[ulp_num]. doorbell_offset = phwi_ctrlr->default_pdu_data[ulp_num].
doorbell_offset; doorbell_offset;
} }
/* number of entries posted must be in multiples of 8 */
if (cons % 8)
return;
writables = (writables / 8) * 8; list_for_each_entry_safe(pasync_handle, tmp, hfree_list, link) {
if (writables) { list_del_init(&pasync_handle->link);
for (i = 0; i < writables; i++) { pasync_handle->is_final = 0;
pbusy_list = pasync_handle->buffer_len = 0;
hwi_get_async_busy_list(pasync_ctx, is_header,
host_write_num);
pasync_handle =
list_entry(pfree_link, struct async_pdu_handle,
link);
WARN_ON(!pasync_handle);
pasync_handle->consumed = 0;
pfree_link = pfree_link->next;
pasync_sge = hwi_get_ring_address(pasync_ctx,
is_header, host_write_num);
/* handles can be consumed out of order, use index in handle */
index = pasync_handle->index;
WARN_ON(pasync_handle->is_header != header);
if (header)
slot = &pasync_ctx->async_entry[index].header;
else
slot = &pasync_ctx->async_entry[index].data;
/**
* The slot just tracks handle's hold and release, so
* overwriting at the same index won't do any harm but
* needs to be caught.
*/
if (*slot != NULL) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
"BM_%d : async PDU %s slot at %u not empty\n",
header ? "header" : "data", index);
}
/**
* We use same freed index as in completion to post so this
* operation is not required for refills. Its required only
* for ring creation.
*/
if (header)
pasync_sge = pasync_ctx->async_header.ring_base;
else
pasync_sge = pasync_ctx->async_data.ring_base;
pasync_sge += index;
/* if its a refill then address is same; hi is lo */
WARN_ON(pasync_sge->hi &&
pasync_sge->hi != pasync_handle->pa.u.a32.address_lo);
WARN_ON(pasync_sge->lo &&
pasync_sge->lo != pasync_handle->pa.u.a32.address_hi);
pasync_sge->hi = pasync_handle->pa.u.a32.address_lo; pasync_sge->hi = pasync_handle->pa.u.a32.address_lo;
pasync_sge->lo = pasync_handle->pa.u.a32.address_hi; pasync_sge->lo = pasync_handle->pa.u.a32.address_hi;
list_move(&pasync_handle->link, pbusy_list); *slot = pasync_handle;
if (++prod == cons)
host_write_num++; break;
host_write_num = host_write_num % num_entries;
}
if (is_header) {
pasync_ctx->async_header.host_write_ptr =
host_write_num;
pasync_ctx->async_header.free_entries -= writables;
pasync_ctx->async_header.writables -= writables;
pasync_ctx->async_header.busy_entries += writables;
} else {
pasync_ctx->async_data.host_write_ptr = host_write_num;
pasync_ctx->async_data.free_entries -= writables;
pasync_ctx->async_data.writables -= writables;
pasync_ctx->async_data.busy_entries += writables;
} }
if (header)
pasync_ctx->async_header.free_entries -= prod;
else
pasync_ctx->async_data.free_entries -= prod;
doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK; doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK;
doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT; doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT;
doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT; doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT;
doorbell |= (writables & DB_DEF_PDU_CQPROC_MASK) doorbell |= (prod & DB_DEF_PDU_CQPROC_MASK) << DB_DEF_PDU_CQPROC_SHIFT;
<< DB_DEF_PDU_CQPROC_SHIFT;
iowrite32(doorbell, phba->db_va + doorbell_offset); iowrite32(doorbell, phba->db_va + doorbell_offset);
}
}
static void hwi_flush_default_pdu_buffer(struct beiscsi_hba *phba,
struct beiscsi_conn *beiscsi_conn,
struct i_t_dpdu_cqe *pdpdu_cqe)
{
struct hwi_controller *phwi_ctrlr;
struct hwi_async_pdu_context *pasync_ctx;
struct async_pdu_handle *pasync_handle = NULL;
unsigned int cq_index = -1;
uint16_t cri_index = BE_GET_CRI_FROM_CID(
beiscsi_conn->beiscsi_conn_cid);
phwi_ctrlr = phba->phwi_ctrlr;
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr,
BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr,
cri_index));
pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx,
pdpdu_cqe, &cq_index);
BUG_ON(pasync_handle->is_header != 0);
if (pasync_handle->consumed == 0)
hwi_update_async_writables(phba, pasync_ctx,
pasync_handle->is_header, cq_index);
hwi_free_async_msg(phba, pasync_ctx, pasync_handle->cri);
hwi_post_async_buffers(phba, pasync_handle->is_header,
BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr,
cri_index));
} }
static unsigned int static void
hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn, beiscsi_hdq_process_compl(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba,
struct hwi_async_pdu_context *pasync_ctx, unsigned short cri)
{
struct list_head *plist;
struct async_pdu_handle *pasync_handle;
void *phdr = NULL;
unsigned int hdr_len = 0, buf_len = 0;
unsigned int status, index = 0, offset = 0;
void *pfirst_buffer = NULL;
unsigned int num_buf = 0;
plist = &pasync_ctx->async_entry[cri].wait_queue.list;
list_for_each_entry(pasync_handle, plist, link) {
if (index == 0) {
phdr = pasync_handle->pbuffer;
hdr_len = pasync_handle->buffer_len;
} else {
buf_len = pasync_handle->buffer_len;
if (!num_buf) {
pfirst_buffer = pasync_handle->pbuffer;
num_buf++;
}
memcpy(pfirst_buffer + offset,
pasync_handle->pbuffer, buf_len);
offset += buf_len;
}
index++;
}
status = beiscsi_process_async_pdu(beiscsi_conn, phba,
phdr, hdr_len, pfirst_buffer,
offset);
hwi_free_async_msg(phba, pasync_ctx, cri);
return 0;
}
static unsigned int
hwi_gather_async_pdu(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba,
struct async_pdu_handle *pasync_handle)
{
struct hwi_async_pdu_context *pasync_ctx;
struct hwi_controller *phwi_ctrlr;
unsigned int bytes_needed = 0, status = 0;
unsigned short cri = pasync_handle->cri;
struct pdu_base *ppdu;
phwi_ctrlr = phba->phwi_ctrlr;
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr,
BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr,
BE_GET_CRI_FROM_CID(beiscsi_conn->
beiscsi_conn_cid)));
list_del(&pasync_handle->link);
if (pasync_handle->is_header) {
pasync_ctx->async_header.busy_entries--;
if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) {
hwi_free_async_msg(phba, pasync_ctx, cri);
BUG();
}
pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0;
pasync_ctx->async_entry[cri].wait_queue.hdr_received = 1;
pasync_ctx->async_entry[cri].wait_queue.hdr_len =
(unsigned short)pasync_handle->buffer_len;
list_add_tail(&pasync_handle->link,
&pasync_ctx->async_entry[cri].wait_queue.list);
ppdu = pasync_handle->pbuffer;
bytes_needed = ((((ppdu->dw[offsetof(struct amap_pdu_base,
data_len_hi) / 32] & PDUBASE_DATALENHI_MASK) << 8) &
0xFFFF0000) | ((be16_to_cpu((ppdu->
dw[offsetof(struct amap_pdu_base, data_len_lo) / 32]
& PDUBASE_DATALENLO_MASK) >> 16)) & 0x0000FFFF));
if (status == 0) {
pasync_ctx->async_entry[cri].wait_queue.bytes_needed =
bytes_needed;
if (bytes_needed == 0)
status = hwi_fwd_async_msg(beiscsi_conn, phba,
pasync_ctx, cri);
}
} else {
pasync_ctx->async_data.busy_entries--;
if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) {
list_add_tail(&pasync_handle->link,
&pasync_ctx->async_entry[cri].wait_queue.
list);
pasync_ctx->async_entry[cri].wait_queue.
bytes_received +=
(unsigned short)pasync_handle->buffer_len;
if (pasync_ctx->async_entry[cri].wait_queue.
bytes_received >=
pasync_ctx->async_entry[cri].wait_queue.
bytes_needed)
status = hwi_fwd_async_msg(beiscsi_conn, phba,
pasync_ctx, cri);
}
}
return status;
}
static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba,
struct i_t_dpdu_cqe *pdpdu_cqe) struct i_t_dpdu_cqe *pdpdu_cqe)
{ {
struct beiscsi_hba *phba = beiscsi_conn->phba;
struct hd_async_handle *pasync_handle = NULL;
struct hd_async_context *pasync_ctx;
struct hwi_controller *phwi_ctrlr; struct hwi_controller *phwi_ctrlr;
struct hwi_async_pdu_context *pasync_ctx; u16 cid_cri;
struct async_pdu_handle *pasync_handle = NULL; u8 ulp_num;
unsigned int cq_index = -1;
uint16_t cri_index = BE_GET_CRI_FROM_CID(
beiscsi_conn->beiscsi_conn_cid);
phwi_ctrlr = phba->phwi_ctrlr; phwi_ctrlr = phba->phwi_ctrlr;
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, cid_cri = BE_GET_CRI_FROM_CID(beiscsi_conn->beiscsi_conn_cid);
BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, ulp_num = BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, cid_cri);
cri_index)); pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num);
pasync_handle = beiscsi_hdl_get_handle(beiscsi_conn, pasync_ctx,
pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, pdpdu_cqe);
pdpdu_cqe, &cq_index); if (!pasync_handle)
return;
if (pasync_handle->consumed == 0)
hwi_update_async_writables(phba, pasync_ctx,
pasync_handle->is_header, cq_index);
hwi_gather_async_pdu(beiscsi_conn, phba, pasync_handle); beiscsi_hdl_gather_pdu(beiscsi_conn, pasync_ctx, pasync_handle);
hwi_post_async_buffers(phba, pasync_handle->is_header, beiscsi_hdq_post_handles(phba, pasync_handle->is_header, ulp_num);
BEISCSI_GET_ULP_FROM_CRI(
phwi_ctrlr, cri_index));
} }
void beiscsi_process_mcc_cq(struct beiscsi_hba *phba) void beiscsi_process_mcc_cq(struct beiscsi_hba *phba)
...@@ -2004,7 +1950,7 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) ...@@ -2004,7 +1950,7 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
cqe_desc[code], code, cid); cqe_desc[code], code, cid);
spin_lock_bh(&phba->async_pdu_lock); spin_lock_bh(&phba->async_pdu_lock);
hwi_process_default_pdu_ring(beiscsi_conn, phba, beiscsi_hdq_process_compl(beiscsi_conn,
(struct i_t_dpdu_cqe *)sol); (struct i_t_dpdu_cqe *)sol);
spin_unlock_bh(&phba->async_pdu_lock); spin_unlock_bh(&phba->async_pdu_lock);
break; break;
...@@ -2015,7 +1961,7 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) ...@@ -2015,7 +1961,7 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
cqe_desc[code], code, cid); cqe_desc[code], code, cid);
spin_lock_bh(&phba->async_pdu_lock); spin_lock_bh(&phba->async_pdu_lock);
hwi_process_default_pdu_ring(beiscsi_conn, phba, beiscsi_hdq_process_compl(beiscsi_conn,
(struct i_t_dpdu_cqe *)sol); (struct i_t_dpdu_cqe *)sol);
spin_unlock_bh(&phba->async_pdu_lock); spin_unlock_bh(&phba->async_pdu_lock);
break; break;
...@@ -2052,8 +1998,9 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) ...@@ -2052,8 +1998,9 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
"BM_%d : Dropping %s[%d] on DPDU ring on CID : %d\n", "BM_%d : Dropping %s[%d] on DPDU ring on CID : %d\n",
cqe_desc[code], code, cid); cqe_desc[code], code, cid);
spin_lock_bh(&phba->async_pdu_lock); spin_lock_bh(&phba->async_pdu_lock);
hwi_flush_default_pdu_buffer(phba, beiscsi_conn, /* driver consumes the entry and drops the contents */
(struct i_t_dpdu_cqe *) sol); beiscsi_hdq_process_compl(beiscsi_conn,
(struct i_t_dpdu_cqe *)sol);
spin_unlock_bh(&phba->async_pdu_lock); spin_unlock_bh(&phba->async_pdu_lock);
break; break;
case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL: case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL:
...@@ -2503,20 +2450,20 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba) ...@@ -2503,20 +2450,20 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
(ulp_num * MEM_DESCR_OFFSET)); (ulp_num * MEM_DESCR_OFFSET));
phba->mem_req[mem_descr_index] = phba->mem_req[mem_descr_index] =
BEISCSI_GET_CID_COUNT(phba, ulp_num) * BEISCSI_GET_CID_COUNT(phba, ulp_num) *
sizeof(struct async_pdu_handle); sizeof(struct hd_async_handle);
mem_descr_index = (HWI_MEM_ASYNC_DATA_HANDLE_ULP0 + mem_descr_index = (HWI_MEM_ASYNC_DATA_HANDLE_ULP0 +
(ulp_num * MEM_DESCR_OFFSET)); (ulp_num * MEM_DESCR_OFFSET));
phba->mem_req[mem_descr_index] = phba->mem_req[mem_descr_index] =
BEISCSI_GET_CID_COUNT(phba, ulp_num) * BEISCSI_GET_CID_COUNT(phba, ulp_num) *
sizeof(struct async_pdu_handle); sizeof(struct hd_async_handle);
mem_descr_index = (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 + mem_descr_index = (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 +
(ulp_num * MEM_DESCR_OFFSET)); (ulp_num * MEM_DESCR_OFFSET));
phba->mem_req[mem_descr_index] = phba->mem_req[mem_descr_index] =
sizeof(struct hwi_async_pdu_context) + sizeof(struct hd_async_context) +
(BEISCSI_GET_CID_COUNT(phba, ulp_num) * (BEISCSI_GET_CID_COUNT(phba, ulp_num) *
sizeof(struct hwi_async_entry)); sizeof(struct hd_async_entry));
} }
} }
} }
...@@ -2775,35 +2722,34 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2775,35 +2722,34 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
uint8_t ulp_num; uint8_t ulp_num;
struct hwi_controller *phwi_ctrlr; struct hwi_controller *phwi_ctrlr;
struct hba_parameters *p = &phba->params; struct hba_parameters *p = &phba->params;
struct hwi_async_pdu_context *pasync_ctx; struct hd_async_context *pasync_ctx;
struct async_pdu_handle *pasync_header_h, *pasync_data_h; struct hd_async_handle *pasync_header_h, *pasync_data_h;
unsigned int index, idx, num_per_mem, num_async_data; unsigned int index, idx, num_per_mem, num_async_data;
struct be_mem_descriptor *mem_descr; struct be_mem_descriptor *mem_descr;
for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
/* get async_ctx for each ULP */
mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr = (struct be_mem_descriptor *)phba->init_mem;
mem_descr += (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 + mem_descr += (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 +
(ulp_num * MEM_DESCR_OFFSET)); (ulp_num * MEM_DESCR_OFFSET));
phwi_ctrlr = phba->phwi_ctrlr; phwi_ctrlr = phba->phwi_ctrlr;
phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num] = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num] =
(struct hwi_async_pdu_context *) (struct hd_async_context *)
mem_descr->mem_array[0].virtual_address; mem_descr->mem_array[0].virtual_address;
pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num]; pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num];
memset(pasync_ctx, 0, sizeof(*pasync_ctx)); memset(pasync_ctx, 0, sizeof(*pasync_ctx));
pasync_ctx->async_entry = pasync_ctx->async_entry =
(struct hwi_async_entry *) (struct hd_async_entry *)
((long unsigned int)pasync_ctx + ((long unsigned int)pasync_ctx +
sizeof(struct hwi_async_pdu_context)); sizeof(struct hd_async_context));
pasync_ctx->num_entries = BEISCSI_GET_CID_COUNT(phba, pasync_ctx->num_entries = BEISCSI_GET_CID_COUNT(phba,
ulp_num); ulp_num);
pasync_ctx->buffer_size = p->defpdu_hdr_sz; /* setup header buffers */
mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr = (struct be_mem_descriptor *)phba->init_mem;
mem_descr += HWI_MEM_ASYNC_HEADER_BUF_ULP0 + mem_descr += HWI_MEM_ASYNC_HEADER_BUF_ULP0 +
(ulp_num * MEM_DESCR_OFFSET); (ulp_num * MEM_DESCR_OFFSET);
...@@ -2820,6 +2766,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2820,6 +2766,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
"BM_%d : No Virtual address for ULP : %d\n", "BM_%d : No Virtual address for ULP : %d\n",
ulp_num); ulp_num);
pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz;
pasync_ctx->async_header.va_base = pasync_ctx->async_header.va_base =
mem_descr->mem_array[0].virtual_address; mem_descr->mem_array[0].virtual_address;
...@@ -2827,6 +2774,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2827,6 +2774,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
mem_descr->mem_array[0]. mem_descr->mem_array[0].
bus_address.u.a64.address; bus_address.u.a64.address;
/* setup header buffer sgls */
mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr = (struct be_mem_descriptor *)phba->init_mem;
mem_descr += HWI_MEM_ASYNC_HEADER_RING_ULP0 + mem_descr += HWI_MEM_ASYNC_HEADER_RING_ULP0 +
(ulp_num * MEM_DESCR_OFFSET); (ulp_num * MEM_DESCR_OFFSET);
...@@ -2846,6 +2794,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2846,6 +2794,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
pasync_ctx->async_header.ring_base = pasync_ctx->async_header.ring_base =
mem_descr->mem_array[0].virtual_address; mem_descr->mem_array[0].virtual_address;
/* setup header buffer handles */
mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr = (struct be_mem_descriptor *)phba->init_mem;
mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE_ULP0 + mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE_ULP0 +
(ulp_num * MEM_DESCR_OFFSET); (ulp_num * MEM_DESCR_OFFSET);
...@@ -2864,9 +2813,9 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2864,9 +2813,9 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
pasync_ctx->async_header.handle_base = pasync_ctx->async_header.handle_base =
mem_descr->mem_array[0].virtual_address; mem_descr->mem_array[0].virtual_address;
pasync_ctx->async_header.writables = 0;
INIT_LIST_HEAD(&pasync_ctx->async_header.free_list); INIT_LIST_HEAD(&pasync_ctx->async_header.free_list);
/* setup data buffer sgls */
mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr = (struct be_mem_descriptor *)phba->init_mem;
mem_descr += HWI_MEM_ASYNC_DATA_RING_ULP0 + mem_descr += HWI_MEM_ASYNC_DATA_RING_ULP0 +
(ulp_num * MEM_DESCR_OFFSET); (ulp_num * MEM_DESCR_OFFSET);
...@@ -2886,6 +2835,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2886,6 +2835,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
pasync_ctx->async_data.ring_base = pasync_ctx->async_data.ring_base =
mem_descr->mem_array[0].virtual_address; mem_descr->mem_array[0].virtual_address;
/* setup data buffer handles */
mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr = (struct be_mem_descriptor *)phba->init_mem;
mem_descr += HWI_MEM_ASYNC_DATA_HANDLE_ULP0 + mem_descr += HWI_MEM_ASYNC_DATA_HANDLE_ULP0 +
(ulp_num * MEM_DESCR_OFFSET); (ulp_num * MEM_DESCR_OFFSET);
...@@ -2897,16 +2847,16 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2897,16 +2847,16 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
pasync_ctx->async_data.handle_base = pasync_ctx->async_data.handle_base =
mem_descr->mem_array[0].virtual_address; mem_descr->mem_array[0].virtual_address;
pasync_ctx->async_data.writables = 0;
INIT_LIST_HEAD(&pasync_ctx->async_data.free_list); INIT_LIST_HEAD(&pasync_ctx->async_data.free_list);
pasync_header_h = pasync_header_h =
(struct async_pdu_handle *) (struct hd_async_handle *)
pasync_ctx->async_header.handle_base; pasync_ctx->async_header.handle_base;
pasync_data_h = pasync_data_h =
(struct async_pdu_handle *) (struct hd_async_handle *)
pasync_ctx->async_data.handle_base; pasync_ctx->async_data.handle_base;
/* setup data buffers */
mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr = (struct be_mem_descriptor *)phba->init_mem;
mem_descr += HWI_MEM_ASYNC_DATA_BUF_ULP0 + mem_descr += HWI_MEM_ASYNC_DATA_BUF_ULP0 +
(ulp_num * MEM_DESCR_OFFSET); (ulp_num * MEM_DESCR_OFFSET);
...@@ -2924,6 +2874,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2924,6 +2874,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
ulp_num); ulp_num);
idx = 0; idx = 0;
pasync_ctx->async_data.buffer_size = p->defpdu_data_sz;
pasync_ctx->async_data.va_base = pasync_ctx->async_data.va_base =
mem_descr->mem_array[idx].virtual_address; mem_descr->mem_array[idx].virtual_address;
pasync_ctx->async_data.pa_base.u.a64.address = pasync_ctx->async_data.pa_base.u.a64.address =
...@@ -2937,7 +2888,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2937,7 +2888,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
for (index = 0; index < BEISCSI_GET_CID_COUNT for (index = 0; index < BEISCSI_GET_CID_COUNT
(phba, ulp_num); index++) { (phba, ulp_num); index++) {
pasync_header_h->cri = -1; pasync_header_h->cri = -1;
pasync_header_h->index = (char)index; pasync_header_h->is_header = 1;
pasync_header_h->index = index;
INIT_LIST_HEAD(&pasync_header_h->link); INIT_LIST_HEAD(&pasync_header_h->link);
pasync_header_h->pbuffer = pasync_header_h->pbuffer =
(void *)((unsigned long) (void *)((unsigned long)
...@@ -2954,14 +2906,13 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2954,14 +2906,13 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
free_list); free_list);
pasync_header_h++; pasync_header_h++;
pasync_ctx->async_header.free_entries++; pasync_ctx->async_header.free_entries++;
pasync_ctx->async_header.writables++;
INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. INIT_LIST_HEAD(&pasync_ctx->async_entry[index].
wait_queue.list); wq.list);
INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. pasync_ctx->async_entry[index].header = NULL;
header_busy_list);
pasync_data_h->cri = -1; pasync_data_h->cri = -1;
pasync_data_h->index = (char)index; pasync_data_h->is_header = 0;
pasync_data_h->index = index;
INIT_LIST_HEAD(&pasync_data_h->link); INIT_LIST_HEAD(&pasync_data_h->link);
if (!num_async_data) { if (!num_async_data) {
...@@ -2996,16 +2947,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ...@@ -2996,16 +2947,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
free_list); free_list);
pasync_data_h++; pasync_data_h++;
pasync_ctx->async_data.free_entries++; pasync_ctx->async_data.free_entries++;
pasync_ctx->async_data.writables++; pasync_ctx->async_entry[index].data = NULL;
INIT_LIST_HEAD(&pasync_ctx->async_entry[index].
data_busy_list);
} }
pasync_ctx->async_header.host_write_ptr = 0;
pasync_ctx->async_header.ep_read_ptr = -1;
pasync_ctx->async_data.host_write_ptr = 0;
pasync_ctx->async_data.ep_read_ptr = -1;
} }
} }
...@@ -3707,7 +3650,7 @@ static void hwi_cleanup_port(struct beiscsi_hba *phba) ...@@ -3707,7 +3650,7 @@ static void hwi_cleanup_port(struct beiscsi_hba *phba)
struct be_ctrl_info *ctrl = &phba->ctrl; struct be_ctrl_info *ctrl = &phba->ctrl;
struct hwi_controller *phwi_ctrlr; struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context; struct hwi_context_memory *phwi_context;
struct hwi_async_pdu_context *pasync_ctx; struct hd_async_context *pasync_ctx;
int i, eq_for_mcc, ulp_num; int i, eq_for_mcc, ulp_num;
for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++)
...@@ -3820,7 +3763,6 @@ static int hwi_init_port(struct beiscsi_hba *phba) ...@@ -3820,7 +3763,6 @@ static int hwi_init_port(struct beiscsi_hba *phba)
for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
def_pdu_ring_sz = def_pdu_ring_sz =
BEISCSI_GET_CID_COUNT(phba, ulp_num) * BEISCSI_GET_CID_COUNT(phba, ulp_num) *
sizeof(struct phys_addr); sizeof(struct phys_addr);
...@@ -3851,9 +3793,9 @@ static int hwi_init_port(struct beiscsi_hba *phba) ...@@ -3851,9 +3793,9 @@ static int hwi_init_port(struct beiscsi_hba *phba)
* let EP know about it. * let EP know about it.
* Call beiscsi_cmd_iscsi_cleanup before posting? * Call beiscsi_cmd_iscsi_cleanup before posting?
*/ */
hwi_post_async_buffers(phba, BEISCSI_DEFQ_HDR, beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR,
ulp_num); ulp_num);
hwi_post_async_buffers(phba, BEISCSI_DEFQ_DATA, beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA,
ulp_num); ulp_num);
} }
} }
...@@ -3883,7 +3825,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) ...@@ -3883,7 +3825,7 @@ static int hwi_init_port(struct beiscsi_hba *phba)
if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
uint16_t cri = 0; uint16_t cri = 0;
struct hwi_async_pdu_context *pasync_ctx; struct hd_async_context *pasync_ctx;
pasync_ctx = HWI_GET_ASYNC_PDU_CTX( pasync_ctx = HWI_GET_ASYNC_PDU_CTX(
phwi_ctrlr, ulp_num); phwi_ctrlr, ulp_num);
...@@ -3895,6 +3837,14 @@ static int hwi_init_port(struct beiscsi_hba *phba) ...@@ -3895,6 +3837,14 @@ static int hwi_init_port(struct beiscsi_hba *phba)
phwi_ctrlr->wrb_context[cri].cid] = phwi_ctrlr->wrb_context[cri].cid] =
async_arr_idx++; async_arr_idx++;
} }
/**
* Now that the default PDU rings have been created,
* let EP know about it.
*/
beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR,
ulp_num);
beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA,
ulp_num);
} }
} }
......
...@@ -608,80 +608,81 @@ struct amap_beiscsi_offload_params { ...@@ -608,80 +608,81 @@ struct amap_beiscsi_offload_params {
u8 max_recv_data_segment_length[32]; u8 max_recv_data_segment_length[32];
}; };
/* void hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, struct hd_async_handle {
struct beiscsi_hba *phba, struct sol_cqe *psol);*/
struct async_pdu_handle {
struct list_head link; struct list_head link;
struct be_bus_address pa; struct be_bus_address pa;
void *pbuffer; void *pbuffer;
unsigned int consumed; u32 buffer_len;
unsigned char index; u16 index;
unsigned char is_header; u16 cri;
unsigned short cri; u8 is_header;
unsigned long buffer_len; u8 is_final;
}; };
struct hwi_async_entry { /**
struct { * This has list of async PDUs that are waiting to be processed.
unsigned char hdr_received; * Buffers live in this list for a brief duration before they get
unsigned char hdr_len; * processed and posted back to hardware.
unsigned short bytes_received; * Note that we don't really need one cri_wait_queue per async_entry.
* We need one cri_wait_queue per CRI. Its easier to manage if this
* is tagged along with the async_entry.
*/
struct hd_async_entry {
struct cri_wait_queue {
unsigned short hdr_len;
unsigned int bytes_received;
unsigned int bytes_needed; unsigned int bytes_needed;
struct list_head list; struct list_head list;
} wait_queue; } wq;
/* handles posted to FW resides here */
struct list_head header_busy_list; struct hd_async_handle *header;
struct list_head data_busy_list; struct hd_async_handle *data;
}; };
struct hwi_async_pdu_context { struct hd_async_buf_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;
struct list_head free_list;
} async_header;
struct {
struct be_bus_address pa_base; struct be_bus_address pa_base;
void *va_base; void *va_base;
void *ring_base; void *ring_base;
struct async_pdu_handle *handle_base; struct hd_async_handle *handle_base;
u16 free_entries;
unsigned int host_write_ptr; u32 buffer_size;
unsigned int ep_read_ptr; /**
unsigned int writables; * Once iSCSI layer finishes processing an async PDU, the
* handles used for the PDU are added to this list.
unsigned int free_entries; * They are posted back to FW in groups of 8.
unsigned int busy_entries; */
struct list_head free_list; struct list_head free_list;
} async_data; };
unsigned int buffer_size; /**
unsigned int num_entries; * hd_async_context is declared for each ULP supporting iSCSI function.
*/
struct hd_async_context {
struct hd_async_buf_context async_header;
struct hd_async_buf_context async_data;
u16 num_entries;
/**
* When unsol PDU is in, it needs to be chained till all the bytes are
* received and then processing is done. hd_async_entry is created
* based on the cid_count for each ULP. When unsol PDU comes in based
* on the conn_id it needs to be added to the correct async_entry wq.
* Below defined cid_to_async_cri_map is used to reterive the
* async_cri_map for a particular connection.
*
* This array is initialized after beiscsi_create_wrb_rings returns.
*
* - this method takes more memory space, fixed to 2K
* - any support for connections greater than this the array size needs
* to be incremented
*/
#define BE_GET_ASYNC_CRI_FROM_CID(cid) (pasync_ctx->cid_to_async_cri_map[cid]) #define BE_GET_ASYNC_CRI_FROM_CID(cid) (pasync_ctx->cid_to_async_cri_map[cid])
unsigned short cid_to_async_cri_map[BE_MAX_SESSION]; unsigned short cid_to_async_cri_map[BE_MAX_SESSION];
/** /**
* This is a varying size list! Do not add anything * This is a variable size array. Don`t add anything after this field!!
* after this entry!!
*/ */
struct hwi_async_entry *async_entry; struct hd_async_entry *async_entry;
}; };
#define PDUCQE_CODE_MASK 0x0000003F
#define PDUCQE_DPL_MASK 0xFFFF0000
#define PDUCQE_INDEX_MASK 0x0000FFFF
struct i_t_dpdu_cqe { struct i_t_dpdu_cqe {
u32 dw[4]; u32 dw[4];
} __packed; } __packed;
...@@ -1077,9 +1078,14 @@ struct hwi_context_memory { ...@@ -1077,9 +1078,14 @@ struct hwi_context_memory {
struct be_queue_info be_cq[MAX_CPUS - 1]; struct be_queue_info be_cq[MAX_CPUS - 1];
struct be_queue_info *be_wrbq; struct be_queue_info *be_wrbq;
/**
* Create array of ULP number for below entries as DEFQ
* will be created for both ULP if iSCSI Protocol is
* loaded on both ULP.
*/
struct be_queue_info be_def_hdrq[BEISCSI_ULP_COUNT]; struct be_queue_info be_def_hdrq[BEISCSI_ULP_COUNT];
struct be_queue_info be_def_dataq[BEISCSI_ULP_COUNT]; struct be_queue_info be_def_dataq[BEISCSI_ULP_COUNT];
struct hwi_async_pdu_context *pasync_ctx[BEISCSI_ULP_COUNT]; struct hd_async_context *pasync_ctx[BEISCSI_ULP_COUNT];
}; };
void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle); void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment