lm_l4fp.c revision d14abf155341d55053c76eeec58b787a456b753b
#include "lm5710.h"
#include "bd_chain.h"
#include "lm_l4fp.h"
#include "mm_l4if.h"
/**
* Description
* complete Rx and Tx application buffers to client
* Assumption:
* Called under fp-lock
* Called only from DPC-Flow
*/
void lm_tcp_complete_bufs(
struct _lm_device_t *pdev,
lm_tcp_state_t *tcp,
lm_tcp_con_t *con)
{
s_list_t completed_bufs;
/* check which buffers need to be completed to client */
s_list_clear(&completed_bufs);
/* should only be here if we have something to complete */
DbgBreakIf(con->dpc_info.dpc_completed_tail == NULL);
s_list_split(&con->active_tb_list, &completed_bufs,
con->dpc_info.dpc_completed_tail, con->dpc_info.dpc_bufs_completed);
con->dpc_info.dpc_completed_tail = NULL;
DbgBreakIf(con->rq_nbytes < con->dpc_info.dpc_actual_bytes_completed);
con->rq_nbytes -= con->dpc_info.dpc_actual_bytes_completed;
lm_bd_chain_bds_consumed(&con->bd_chain, con->dpc_info.dpc_bd_used);
con->dpc_info.dpc_bd_used = 0;
con->dpc_info.dpc_bufs_completed = 0;
con->buffer_completed_cnt += s_list_entry_cnt(&completed_bufs);
DbgMessage(pdev, VERBOSEl4fp,
"cid=%d, completing %d bufs towards mm, actual_completed_bytes=%d, %d bufs still in active tb list\n",
tcp->cid, s_list_entry_cnt(&completed_bufs), con->dpc_info.dpc_actual_bytes_completed, s_list_entry_cnt(&con->active_tb_list));
con->dpc_info.dpc_actual_bytes_completed = 0;
/* GilR 5/10/2006 - TBD - Might need some kind of indicating policy towards the mm - i.e. indicate MaxIndicationLimit at a time*/
DbgBreakIf(s_list_is_empty(&completed_bufs));
if (!con->rq_completion_calls) {
lm_tcp_buffer_t *tcp_buf = (lm_tcp_buffer_t *)s_list_peek_head(&completed_bufs);
if (tcp_buf->flags & TCP_BUF_FLAG_L4_PARTIAL_FILLED) {
RESET_FLAGS(con->db_data.rx->flags, TOE_RX_DB_DATA_PARTIAL_FILLED_BUF);
}
}
con->rq_completion_calls++;
mm_tcp_complete_bufs(pdev, tcp, con, &completed_bufs, LM_STATUS_SUCCESS);
}
/** Description:
* Complete nbytes from Tx and Rx application buffers
* Assumptions:
* Called ONLY from dpc-flow (or deferred_cqes) not POST - flow
* Called W/O a lock (unless from deferred)
* push can have 3 values:
* - 0: no-push
* - 1: regular push
* - 2: push as a result of terminate / reset / fin...
* Returns:
* Actual bytes completed towards mm. (If push==0 this number is equal to
* given completed_bytes, and if push==1 it might be larger), if fin is received this
* may be smaller than completed_bytes by a maximum of '1' */
u32_t lm_tcp_complete_nbytes(
struct _lm_device_t *pdev,
lm_tcp_state_t *tcp,
lm_tcp_con_t *con, /* Rx OR Tx connection */
u32_t completed_bytes, /* num bytes completed (might be 0) */
u8_t push /* if == 0, don't complete partialy
completed buffers towards mm. If (1) - regular push, if (2) push as result of sp-completion (terminate for example) */)
{
lm_tcp_buffer_t * tcp_buf = lm_tcp_next_entry_dpc_active_list(con); /* tcp_buf is the next buffer after tail... */
u32_t actual_completed_bytes = completed_bytes;
u8_t dbg_no_more_bufs = FALSE;
UNREFERENCED_PARAMETER_(tcp);
DbgMessage(pdev, VERBOSEl4fp, "#lm_tcp_complete_bufs\n");
/* we now have completed_bytes on RQ ( could be a result of copying from GRQ (in case of rx) or a regular rq-completion */
con->dpc_info.dpc_rq_placed_bytes += completed_bytes;
DbgBreakIf((con->type == TCP_CON_TYPE_RX) && !tcp_buf); /* RX: even if completed_bytes==0 */
/* Tx: tcp_buf can be NULL since this can be a fin completion
*/
while(tcp_buf && tcp_buf->more_to_comp <= completed_bytes) { /* buffer fully completed */
DbgBreakIf((tcp_buf->more_to_comp == tcp_buf->size) &&
!(tcp_buf->flags & TCP_BUF_FLAG_L4_POST_START ?
con->app_buf_bytes_acc_comp == 0 :
con->app_buf_bytes_acc_comp > 0));
completed_bytes -= tcp_buf->more_to_comp;
con->app_buf_bytes_acc_comp += tcp_buf->more_to_comp;
tcp_buf->more_to_comp = 0; /* essential */
/* complete buffer */
con->dpc_info.dpc_completed_tail = &tcp_buf->link; /* last tcp_buf that needs to be completed */
con->dpc_info.dpc_bd_used += tcp_buf->bd_used;
con->dpc_info.dpc_bufs_completed += 1;
con->dpc_info.dpc_actual_bytes_completed += tcp_buf->size;
if(tcp_buf->flags & TCP_BUF_FLAG_L4_POST_END) {
tcp_buf->app_buf_xferred = con->app_buf_bytes_acc_comp;
DbgBreakIf(tcp_buf->app_buf_xferred != tcp_buf->app_buf_size); /* this is NOT partial completion */
con->app_buf_bytes_acc_comp = 0;
} else {
if (tcp_buf->flags & TCP_BUF_FLAG_L4_SPLIT) {
/* we've completed a split buffer */
DbgBreakIf(GET_FLAGS(con->flags, TCP_POST_DELAYED) == 0);
con->dpc_info.dpc_unblock_post = TRUE;
RESET_FLAGS(tcp_buf->flags, TCP_BUF_FLAG_L4_SPLIT);
dbg_no_more_bufs = TRUE; /* we don't expect any more buffers after this one... */
}
tcp_buf->app_buf_xferred = 0;
}
tcp_buf = (lm_tcp_buffer_t *)s_list_next_entry(&tcp_buf->link);
DbgBreakIf((con->type == TCP_CON_TYPE_RX) && completed_bytes && !tcp_buf);
DbgBreakIf((con->type == TCP_CON_TYPE_TX) && completed_bytes > 1 && !tcp_buf); /* could be 1 if fin */
DbgBreakIf(tcp_buf && dbg_no_more_bufs);
}
if(tcp_buf) { /* possibly, partialy completed buffer */
DbgBreakIf((tcp_buf->more_to_comp == tcp_buf->size) &&
!(tcp_buf->flags & TCP_BUF_FLAG_L4_POST_START ?
con->app_buf_bytes_acc_comp == 0 :
con->app_buf_bytes_acc_comp > 0));
tcp_buf->more_to_comp -= completed_bytes;
con->app_buf_bytes_acc_comp += completed_bytes;
completed_bytes = 0;
/* special care if push==1 AND some bytes were really completed for this buf */
if(push && ((tcp_buf->flags & TCP_BUF_FLAG_L4_PARTIAL_FILLED) || (con->app_buf_bytes_acc_comp > 0)) ) {
DbgBreakIf(con->type != TCP_CON_TYPE_RX); /* push is relevant for Rx con only */
DbgBreakIf((push == 1) && (tcp_buf->flags & TCP_BUF_FLAG_L4_RX_NO_PUSH));
/* skip TBs untill end of app buff - note, it's possible we don't have an end buff in case of
* large split buffers, in this case we'll hit the tcp buffer with the "reserved" flag, we then
* need to mark the connection as being in the middle of completing a split buffer - meaning every
* new buffer that will arrive will be immediately completed until the one with 'end' arrives...
* terrible -but there is no elegant way to deal with large split buffers... */
do {
tcp_buf = lm_tcp_next_entry_dpc_active_list(con);
DbgBreakIf(!tcp_buf); /* push only comes from FW. Therefore:
- we can't reach this place from a peninsula to rq copy completion
- Since we do not post partial app bufs to the FW, if we get here
it is only after the entire app buff is attached to the bd chain */
actual_completed_bytes += tcp_buf->more_to_comp;
con->bytes_push_skip_cnt += tcp_buf->more_to_comp; /* how many bytes did we skip? */
tcp_buf->more_to_comp = 0;
con->partially_completed_buf_cnt++;
/* complete buffer */
con->dpc_info.dpc_completed_tail = &tcp_buf->link;
con->dpc_info.dpc_bd_used += tcp_buf->bd_used;
con->dpc_info.dpc_bufs_completed += 1;
con->dpc_info.dpc_actual_bytes_completed += tcp_buf->size;
} while ( !(GET_FLAGS(tcp_buf->flags, TCP_BUF_FLAG_L4_POST_END)) && !(GET_FLAGS(tcp_buf->flags, TCP_BUF_FLAG_L4_SPLIT)) );
if (GET_FLAGS(tcp_buf->flags, TCP_BUF_FLAG_L4_SPLIT)) {
/* we've completed a split buffer */
DbgBreakIf(GET_FLAGS(con->flags, TCP_POST_DELAYED) == 0);
/* mark connection as "complete next split buffers" , in the meantime this connection is delayed, so post won't look
* at this flag it's safe to change it lockless */
SET_FLAGS(con->flags, TCP_POST_COMPLETE_SPLIT);
con->dpc_info.dpc_unblock_post = TRUE;
RESET_FLAGS(tcp_buf->flags ,TCP_BUF_FLAG_L4_SPLIT); /* this is everest internal, don't want miniport looking at this... */
} else {
tcp_buf->app_buf_xferred = con->app_buf_bytes_acc_comp;
DbgBreakIf(tcp_buf->app_buf_xferred >= tcp_buf->app_buf_size); /* this is partial completion */
con->app_buf_bytes_acc_comp = 0;
}
}
}
/* if all bytes were completed, completed_bytes should be zero. The only case that it won't be zero is if
* one of the completion bytes was a 'fin' completion (TX only). In this case, completed_bytes will be '1'
* In Rx Case, completed_bytes must always be zero. */
DbgBreakIf((con->type == TCP_CON_TYPE_RX) && (completed_bytes != 0));
DbgBreakIf((con->type == TCP_CON_TYPE_TX) && (completed_bytes > 1));
return actual_completed_bytes - completed_bytes;
} /* lm_tcp_complete_nbytes */
void lm_tcp_abort_bufs(
struct _lm_device_t * pdev,
lm_tcp_state_t * tcp,
lm_tcp_con_t * con,
lm_status_t stat
)
{
lm_tcp_buffer_t * tcp_buf;
s_list_entry_t * lentry_p;
s_list_t tmp_list;
DbgBreakIf( ! (pdev && con) );
DbgMessage(pdev, INFORMl4,
"#lm_tcp_abort_bufs: tcp=%p, con type=%d, stat=%d\n",
tcp, con->type, stat);
s_list_init(&tmp_list, NULL, NULL, 0);
/* we don't expect there to be any pending completions... (unless we're in error recovery) */
if (!lm_reset_is_inprogress(pdev))
{
DbgBreakIf ((con->type == TCP_CON_TYPE_RX) && (con->u.rx.skp_bytes_copied));
}
/* If there is completed data, report it in the first seen END-buffer.
There must be at most one not completed App. Buf.
*/
lentry_p = s_list_pop_head(&con->active_tb_list);
while( lentry_p) {
tcp_buf = (lm_tcp_buffer_t *)lentry_p;
con->rq_nbytes -= tcp_buf->size;
tcp_buf->app_buf_xferred = 0;
/* Take care of partially completed buffer */
if (tcp_buf->flags & TCP_BUF_FLAG_L4_POST_END) {
tcp_buf->app_buf_xferred = con->app_buf_bytes_acc_comp;
DbgBreakIf(tcp_buf->app_buf_size < con->app_buf_bytes_acc_comp);
con->app_buf_bytes_acc_comp = 0;
DbgBreakIf(S32_SUB(S64_SUB(con->bytes_post_cnt, con->bytes_comp_cnt), (tcp_buf->app_buf_size - tcp_buf->app_buf_xferred)) < 0);
con->bytes_comp_cnt += (tcp_buf->app_buf_size - tcp_buf->app_buf_xferred);
con->bytes_aborted_cnt += (tcp_buf->app_buf_size - tcp_buf->app_buf_xferred);
}
s_list_push_tail(&tmp_list, &tcp_buf->link);
lentry_p = s_list_pop_head(&con->active_tb_list);
}
/* GilR 8/3/2006 - TODO - can't assert here. pending might be 1 if fin request was posted and not completed (tx con) */
//DbgBreakIf(con->pending_bytes);
/* Complete all buffers from active_list */
if(s_list_entry_cnt(&tmp_list)) {
con->buffer_aborted_cnt += s_list_entry_cnt(&tmp_list);
if (lm_fl_reset_is_inprogress(pdev)) {
con->abortion_under_flr++;
}
mm_tcp_complete_bufs(pdev, tcp, con, &tmp_list, stat);
}
con->flags |= TCP_BUFFERS_ABORTED;
/* Abort all pending buffers in UM */
mm_tcp_abort_bufs(pdev,tcp,con,stat);
DbgBreakIf(!s_list_is_empty(&con->active_tb_list));
}
/******** qe_buffer interface: cyclic NON-OVERRIDE buffer ****************/
/** Description
* returns the next cqe in the cqe_buffer and updates the buffer params
*/
char * lm_tcp_qe_buffer_next_free_cqe(lm_tcp_qe_buffer_t * cqe_buffer)
{
char * cqe;
cqe = cqe_buffer->head;
if(cqe == cqe_buffer->last) {
cqe_buffer->head = cqe_buffer->first; /* cyclic*/
} else {
cqe_buffer->head = cqe + cqe_buffer->qe_size;
}
DbgBreakIf(cqe_buffer->left == 0);
cqe_buffer->left--;
return cqe;
}
/** Description
* returns the next occupied cqe in the cqe_buffer and updates the buffer params
* (tail)
*/
char * lm_tcp_qe_buffer_next_occupied_cqe(lm_tcp_qe_buffer_t * cqe_buffer)
{
char * cqe;
cqe = cqe_buffer->tail;
if ((cqe == cqe_buffer->head) && (cqe_buffer->left > 0)) {
return NULL;
}
if(cqe == cqe_buffer->last) {
cqe_buffer->tail = cqe_buffer->first; /* cyclic*/
} else {
cqe_buffer->tail = cqe + cqe_buffer->qe_size;
}
cqe_buffer->left++;
return cqe;
}
u8_t lm_tcp_qe_buffer_is_empty(lm_tcp_qe_buffer_t * cqe_buffer)
{
return ((cqe_buffer->head == cqe_buffer->tail) && (cqe_buffer->left > 0));
}
/******** qe_buffer interface: cyclic OVERRIDE buffer ****************/
char * lm_tcp_qe_buffer_next_cqe_override(lm_tcp_qe_buffer_t * cqe_buffer)
{
char * cqe;
cqe = cqe_buffer->head;
if(cqe == cqe_buffer->last) {
cqe_buffer->head = cqe_buffer->first; /* cyclic*/
} else {
cqe_buffer->head = cqe + cqe_buffer->qe_size;
}
if (cqe_buffer->left) {
cqe_buffer->left--;
}
return cqe;
}