root/usr/src/uts/common/io/bnxe/577xx/drivers/common/lm/device/bnxe_context.c

/*
functions for managing Chip per-connection context
*/
#include "context.h"
#include "command.h"
#include "cdu_def.h"
#include "bd_chain.h"

/* returns a pionter to a connections chip context*/
void * lm_get_context(struct _lm_device_t *pdev, u32_t cid){

    void * ret = NULL;
    u32_t page,off;

    DbgBreakIf(cid > pdev->params.max_func_connections);
    DbgBreakIf(pdev->context_info->array[cid].invalid != LM_CONTEXT_VALID);

    /* calculate which context page the CID is on*/
    page = cid / (pdev->params.num_context_in_page);

    /* calculate at what offset inside the page CID is on*/
    off = cid % (pdev->params.num_context_in_page);

    /* now goto page,off */
    ret = (void*)((char*)pdev->vars.context_cdu_virt_addr_table[page] + (pdev->params.context_line_size * off));
    /* warrning, this assumes context line size is in chars, need to check!!!*/

    return ret;
}

/* same as above but returns phys address in 64 bit pointer */
u64_t lm_get_context_phys(struct _lm_device_t *pdev, u32_t cid){

    u64_t ret = 0;
    u32_t page,off;

    DbgBreakIf(cid > pdev->params.max_func_connections);
    DbgBreakIf(pdev->context_info->array[cid].invalid != LM_CONTEXT_VALID);

    /* calculate which context page the CID is on*/
    page = cid / (pdev->params.num_context_in_page);

    /* calculate at what offset inside the page CID is on*/
    off = cid % (pdev->params.num_context_in_page);

    /* now goto page,off */
    ret = (pdev->vars.context_cdu_phys_addr_table[page].as_u64 + (pdev->params.context_line_size * off));
    /* warrning, this assumes context line size is in chars, need to check!!!*/

    return ret;
}

extern u32_t LOG2(u32_t v);
static lm_status_t lm_setup_searcher_hash_info(struct _lm_device_t *pdev)
{
    u32_t                    num_con    = 0 ;
    u32_t                    alloc_size = 0 ;
    lm_context_info_t*       context    = NULL;
    lm_searcher_hash_info_t* hash_info  = NULL;
    int                      offset     = 0 ;

    /* sanity */
    if ( CHK_NULL(pdev) || CHK_NULL( pdev->context_info ) )
    {
        DbgBreakMsg("Invalid Parameters") ;
        return LM_STATUS_INVALID_PARAMETER ;
    }
    context   = pdev->context_info;
    hash_info = &context->searcher_hash;

    DbgBreakIf(!pdev->params.max_func_connections);

    if CHK_NULL( hash_info->searcher_table)
    {
        DbgBreakIf(!( hash_info->searcher_table));
        return LM_STATUS_FAILURE;
    }
    num_con    = pdev->params.max_func_connections;
    alloc_size = sizeof(lm_searcher_hash_entry_t) * num_con;
    mm_mem_zero(hash_info->searcher_table, alloc_size);

    /* init value for searcher key */
    // TODO: for now a fixed key, need to change at runtime
    *(u32_t *)(&hash_info->searcher_key[0])  = 0x63285672;
    *(u32_t *)(&hash_info->searcher_key[4])  = 0x24B8F2CC;
    *(u32_t *)(&hash_info->searcher_key[8])  = 0x223AEF9B;
    *(u32_t *)(&hash_info->searcher_key[12]) = 0x26001E3A;
    *(u32_t *)(&hash_info->searcher_key[16]) = 0x7AE91116;
    *(u32_t *)(&hash_info->searcher_key[20]) = 0x5CE5230B;
    *(u32_t *)(&hash_info->searcher_key[24]) = 0x298D8ADF;
    *(u32_t *)(&hash_info->searcher_key[28]) = 0x6EB0FF09;
    *(u32_t *)(&hash_info->searcher_key[32]) = 0x1830F82F;
    *(u32_t *)(&hash_info->searcher_key[36]) = 0x1E46BE7;

    /* Microsoft's example key */
//      *(u32_t *)(&hash_info->searcher_key[0]) = 0xda565a6d;
//      *(u32_t *)(&hash_info->searcher_key[4]) = 0xc20e5b25;
//      *(u32_t *)(&hash_info->searcher_key[8]) = 0x3d256741;
//      *(u32_t *)(&hash_info->searcher_key[12]) = 0xb08fa343;
//      *(u32_t *)(&hash_info->searcher_key[16]) = 0xcb2bcad0;
//      *(u32_t *)(&hash_info->searcher_key[20]) = 0xb4307bae;
//      *(u32_t *)(&hash_info->searcher_key[24]) = 0xa32dcb77;
//      *(u32_t *)(&hash_info->searcher_key[28]) = 0x0cf23080;
//      *(u32_t *)(&hash_info->searcher_key[32]) = 0x3bb7426a;
//      *(u32_t *)(&hash_info->searcher_key[36]) = 0xfa01acbe;

    /* init searcher_key_bits array */
    for (offset = 0; offset < 10; offset++)
    {
        int j,k;
        u32_t bitsOffset = 32*offset;
        u8_t _byte;

        for (j= 0; j < 4; j++)
        {
            _byte  = (u8_t)((*(u32_t *)(&hash_info->searcher_key[offset*4]) >> (j*8)) & 0xff);
            for (k = 0; k < 8; k++)
            {
                hash_info->searcher_key_bits[bitsOffset+(j*8)+k] = ((_byte<<(k%8))& 0x80) ? 1 : 0;
            }
        }
    }

    /* init value for num hash bits */
    hash_info->num_hash_bits = (u8_t)LOG2(num_con);

    return LM_STATUS_SUCCESS ;
}

static lm_status_t lm_alloc_searcher_hash_info(struct _lm_device_t *pdev)
{
    u32_t                    num_con    = 0 ;
    u32_t                    alloc_size = 0 ;
    lm_searcher_hash_info_t* hash_info  = NULL ;
    u8_t                     mm_cli_idx = 0 ;

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER ;
    }

    mm_cli_idx = LM_RESOURCE_COMMON;//!!DP mm_cli_idx_to_um_idx(LM_CLI_IDX_MAX);

    /* searcher is defined with per-function #connections */
    num_con    = pdev->params.max_func_connections;
    alloc_size = sizeof(lm_searcher_hash_entry_t) * num_con;

    hash_info  = &pdev->context_info->searcher_hash;

    if CHK_NULL(hash_info)
    {
        return LM_STATUS_INVALID_PARAMETER ;
    }

    /* allocate searcher mirror hash table */
    hash_info->searcher_table = mm_alloc_mem(pdev, alloc_size, mm_cli_idx);

    if CHK_NULL( hash_info->searcher_table )
    {
        DbgBreakIf(DBG_BREAK_ON(MEMORY_ALLOCATION_FAILURE));
        return  LM_STATUS_RESOURCE ;
    }
    return LM_STATUS_SUCCESS ;
}

lm_status_t lm_init_cid_resc(struct _lm_device_t *pdev, u32_t cid)
{
    lm_cid_resc_t *cid_resc = NULL;
    int            i        = 0;

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    cid_resc = &pdev->context_info->array[cid].cid_resc;
    if CHK_NULL(cid_resc)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    for (i = 0; i < ARRSIZE(cid_resc->cookies); i++)
    {
        cid_resc->cookies[i] = NULL;
    }

    cid_resc->cid_pending = LM_CID_STATE_VALID;
    lm_sp_req_manager_init(pdev, cid);

    return LM_STATUS_SUCCESS;
}

lm_status_t lm_setup_context_pool(struct _lm_device_t *pdev)
{
    u32_t                     num_con         = 0;
    lm_context_info_t *       context         = NULL ;
    u32_t                     i,j;
    struct lm_context_cookie* array           = NULL ;
    lm_searcher_hash_entry_t* searcher_table  = NULL ;

    if CHK_NULL(pdev)
    {
        DbgBreakIf(!pdev);
        return LM_STATUS_INVALID_PARAMETER;
    }

    context = pdev->context_info;

    if CHK_NULL(context)
    {
        DbgBreakIf( context == NULL );
        return LM_STATUS_INVALID_PARAMETER;
    }

    num_con = pdev->params.max_func_connections;

    array           = context->array ;
    searcher_table  = context->searcher_hash.searcher_table ;

    mm_mem_zero( context, sizeof(lm_context_info_t) ) ;

    context->array                        = array ;
    context->searcher_hash.searcher_table = searcher_table ;

    context->proto_start[ETH_CONNECTION_TYPE]   = 0;
    context->proto_end  [ETH_CONNECTION_TYPE]   = pdev->params.max_eth_including_vfs_conns - 1;
    context->proto_start[TOE_CONNECTION_TYPE]   = context->proto_end  [ETH_CONNECTION_TYPE]   + 1;
    context->proto_end  [TOE_CONNECTION_TYPE]   = context->proto_start[TOE_CONNECTION_TYPE]   + pdev->params.max_func_toe_cons - 1;
    context->proto_start[RDMA_CONNECTION_TYPE]  = context->proto_end  [TOE_CONNECTION_TYPE]   + 1;
    context->proto_end  [RDMA_CONNECTION_TYPE]  = context->proto_start[RDMA_CONNECTION_TYPE]  + pdev->params.max_func_rdma_cons - 1;
    context->proto_start[ISCSI_CONNECTION_TYPE] = context->proto_end  [RDMA_CONNECTION_TYPE]  + 1;
    context->proto_end  [ISCSI_CONNECTION_TYPE] = context->proto_start[ISCSI_CONNECTION_TYPE] + pdev->params.max_func_iscsi_cons - 1;
    context->proto_start[FCOE_CONNECTION_TYPE]  = context->proto_end  [ISCSI_CONNECTION_TYPE] + 1;
    context->proto_end  [FCOE_CONNECTION_TYPE]  = context->proto_start[FCOE_CONNECTION_TYPE]  + pdev->params.max_func_fcoe_cons - 1;
    DbgBreakIf(context->proto_end[MAX_PROTO - 1] > pdev->params.max_func_connections -1);

    if CHK_NULL(context->array)
    {
        DbgBreakIf(!( context->array));
        return LM_STATUS_INVALID_PARAMETER;
    }

    mm_mem_zero(context->array, sizeof(struct lm_context_cookie)*num_con);

    ASSERT_STATIC( ARRSIZE(context->proto_start) == ARRSIZE(context->proto_end) );

    /* zero cookies and populate the free lists */
    for (i = 0; i < ARRSIZE(context->proto_start); i++ )
    {
        for (j = context->proto_start[i]; j <= context->proto_end[i]; j++)
        {
            context->array[j].next    = j+1;
            context->array[j].invalid = LM_CONTEXT_VALID;
            context->array[j].ip_type = 0;
            context->array[j].h_val   = 0;
            lm_init_cid_resc(pdev, j);
        }
        /* set the first free item if max_func_XX_cons > 0 */
        if (context->proto_start[i] <= context->proto_end[i]) {
            context->proto_ffree[i] = context->proto_start[i];
        }
        else
        {
            context->proto_ffree[i] = 0;
        }
        context->proto_pending[i] = 0;
        /* put 0 (end of freelist in the last entry for the proto */
        context->array[context->proto_end[i]].next = 0;
    }
    //The ETH cid doorbell space was remapped just fixing the pointers.
    for (j = context->proto_start[ETH_CONNECTION_TYPE]; j <= context->proto_end[ETH_CONNECTION_TYPE]; j++)
    {
#ifdef VF_INVOLVED
        if (IS_CHANNEL_VFDEV(pdev)) {
            context->array[j].cid_resc.mapped_cid_bar_addr =
                (volatile void *)((u8_t*)pdev->vars.mapped_bar_addr[BAR_0] + j*lm_vf_get_doorbell_size(pdev) + VF_BAR0_DB_OFFSET);
#ifdef __SunOS
            context->array[j].cid_resc.reg_handle = pdev->vars.reg_handle[BAR_0];
#endif /* __SunOS */
        } else
#endif /* VF_INVOLVED */
        {
            context->array[j].cid_resc.mapped_cid_bar_addr =
                (volatile void *)((u8_t*)pdev->vars.mapped_bar_addr[BAR_1] + j*LM_DQ_CID_SIZE);
#ifdef __SunOS
            context->array[j].cid_resc.reg_handle = pdev->vars.reg_handle[BAR_1];
#endif /* __SunOS */
        }
    }
    return lm_setup_searcher_hash_info(pdev) ;
}

/* context pool initializer */
lm_status_t lm_alloc_context_pool(struct _lm_device_t *pdev){

    u32_t               num_con    = 0 ;
    lm_context_info_t * context    = NULL ;
    u8_t                mm_cli_idx = 0;

    if CHK_NULL(pdev)
    {
        DbgBreakIf(!pdev);
        return LM_STATUS_INVALID_PARAMETER ;
    }

    /* must not be called if allready initialized */
    if ERR_IF( NULL != pdev->context_info )
    {
        DbgBreakIf( pdev->context_info != NULL ) ;
        return LM_STATUS_FAILURE ;
    }

    mm_cli_idx = LM_RESOURCE_COMMON;//!!DP mm_cli_idx_to_um_idx(LM_CLI_IDX_MAX);

    /* number of context is per-function, the cdu has a per-port register that can be set to be higher than the max_func_connections, but
     * the amount of memory actually allocated for the CDU matches max_func_connections. */
    num_con = pdev->params.max_func_connections ;

    /* allocate context info and cookie array */
    context = mm_alloc_mem(pdev, sizeof(lm_context_info_t), mm_cli_idx);
    if CHK_NULL(context)
    {
        DbgBreakIf(DBG_BREAK_ON(MEMORY_ALLOCATION_FAILURE));
        return LM_STATUS_RESOURCE ;
    }

    /* allocate list entries */
    context->array = mm_alloc_mem(pdev, sizeof(struct lm_context_cookie)*num_con, mm_cli_idx);
    if CHK_NULL(context->array)
    {
        DbgBreakIf(DBG_BREAK_ON(MEMORY_ALLOCATION_FAILURE));
        return LM_STATUS_RESOURCE ;
    }

    /* initilize the lock */

    /* put the context where it belongs */
    pdev->context_info = context;

    /* init searcher hash info */
    return lm_alloc_searcher_hash_info(pdev);
    /* return success */
}

/* context pool release function */
void lm_release_context_pool(struct _lm_device_t *pdev){

    lm_context_info_t* context = NULL;
    u32_t i, j;

    /* must only be called if initialized */
    DbgBreakIf( pdev->context_info == NULL );

    /* first make a copy and kill the original refference */
    context            = pdev->context_info;
    pdev->context_info = NULL;

    /* free context cookie array
       sanity check: scan it and make sure it is empty */
    for (i=0; i<(pdev->params.max_func_connections); i++  )
    {
        for (j = 0; j < MAX_PROTO; j++)
        {
            DbgBreakIf( context->array[i].cid_resc.cookies[j] != NULL );
        }

        /* NirV: can't call from here, context_info is NULL */
        /*DbgBreakIf(lm_sp_req_manager_shutdown(pdev, i) != LM_STATUS_SUCCESS);*/
    }
    /* mm_free_mem(context->array); */

    /* sanity check - searcher mirror hash must be empty */
    DbgBreakIf(context->searcher_hash.num_tuples);

    /* de-initilize the lock? if in debug mode we can leave it taken to chatch errors */

    /* free context info */
    /* mm_free_mem(context); */


    /* return success */

}

static u32_t _lm_searcher_mirror_hash_calc(lm_searcher_hash_info_t *hash_info, lm_4tuple_t *tuple)
{
    u8_t  in_str[MAX_SEARCHER_IN_STR] = {0};
    u8_t* in_str_bits                 = hash_info->searcher_in_str_bits;
    u8_t* key_bits                    = hash_info->searcher_key_bits;
    u32_t in_bits                     = 0;
    u32_t result                      = 0;
    u16_t i                           = 0;
    u16_t j                           = 0;

    /* prepare input string */
    if (tuple->ip_type == LM_IP_TYPE_V4)
    {
        *(u32_t *)(&in_str[0])  = HTON32(tuple->src_ip[0]);
        *(u32_t *)(&in_str[4])  = HTON32(tuple->dst_ip[0]);
        *(u16_t *)(&in_str[8])  = tuple->src_port;
        *(u16_t *)(&in_str[10]) = tuple->dst_port;
        in_bits = 12 * 8;
    }
    else
    {
        *(u32_t *)(&in_str[0])   = HTON32(tuple->src_ip[0]);
        *(u32_t *)(&in_str[4])   = HTON32(tuple->src_ip[1]);
        *(u32_t *)(&in_str[8])   = HTON32(tuple->src_ip[2]);
        *(u32_t *)(&in_str[12])  = HTON32(tuple->src_ip[3]);

        *(u32_t *)(&in_str[16])  = HTON32(tuple->dst_ip[0]);
        *(u32_t *)(&in_str[20])  = HTON32(tuple->dst_ip[1]);
        *(u32_t *)(&in_str[24])  = HTON32(tuple->dst_ip[2]);
        *(u32_t *)(&in_str[28])  = HTON32(tuple->dst_ip[3]);

        *(u16_t *)(&in_str[32]) = tuple->src_port;
        *(u16_t *)(&in_str[34]) = tuple->dst_port;
        in_bits = 36 * 8;
    }

    /* prepare searcher_in_str_bits from in_str */
    for (i = 0; i < in_bits; i++)
    {
        /* 0x80 - the leftmost bit. */
        in_str_bits[i] = ((in_str[i/8]<<(i%8)) & 0x80) ? 1 : 0;
    }

    /* calc ToeplitzHash */
    for (i = 0; i < 32; i++)
    {
        u8_t h = 0;

        for (j = 0; j < in_bits; j++)
        {
            h ^= key_bits[i+j] & in_str_bits[j];
        }

        result |= (h<<(32-i-1));
    }

    return result;
}

/* assumption: CID lock NOT taken by caller */
lm_status_t lm_searcher_mirror_hash_insert(struct _lm_device_t *pdev, u32_t cid, lm_4tuple_t *tuple)
{
    lm_context_info_t        *context    = NULL;
    lm_searcher_hash_entry_t *hash_entry = NULL;
    u32_t                    h_val       = 0;
    u8_t temp_ipv6, temp_ipv4, temp_depth_ipv4, is_ipv4;
    lm_status_t              lm_status   = LM_STATUS_SUCCESS;
    #define SRC_HASH_DEPTH_TH 15 /* that is searcher's default MaxNumHops - 1 */

    /* take spinlock */
    MM_ACQUIRE_CID_LOCK(pdev);

    context = pdev->context_info;
    is_ipv4 = (tuple->ip_type == LM_IP_TYPE_V4 ? 1 : 0);

    /* calc hash val */
    h_val = _lm_searcher_mirror_hash_calc(&context->searcher_hash, tuple);

    /* take only num_hash_bits LSBs */
    h_val &= ((1 << context->searcher_hash.num_hash_bits) - 1);

    /* init num_hash_bits in the searcher: if the h_val is all FFFFs - set it to 0 */
    if (h_val == ((1 << context->searcher_hash.num_hash_bits) - 1)) {
        h_val = 0;
    }

    /* get the hash entry */
    hash_entry = &context->searcher_hash.searcher_table[h_val];

    /* start the alg. to find if there is a place available in that entry */
    temp_ipv6 = hash_entry->num_ipv6 + (is_ipv4 ? 0 : 1);
    temp_ipv4 = hash_entry->num_ipv4 + is_ipv4;

    /* tempDepthIpv4 = max ( depthIpv4(H), roundup(tempIpv4/2) ) */
    temp_depth_ipv4 = (temp_ipv4 / 2) + (temp_ipv4 % 2);
    if (temp_depth_ipv4 < hash_entry->depth_ipv4) {
        temp_depth_ipv4 = hash_entry->depth_ipv4;
    }

    if (temp_depth_ipv4 + temp_ipv6 > SRC_HASH_DEPTH_TH) {
        /* each hash entry has SRC_HASH_DEPTH_TH available places.
         * each place can contain 1 ipv6 connection or 2 ipv4 connections */
        DbgBreakMsg("Reached searcher hash limit\n");
        lm_status = LM_STATUS_FAILURE;
    } else {
        hash_entry->num_ipv6 = temp_ipv6;
        hash_entry->num_ipv4 = temp_ipv4;
        hash_entry->depth_ipv4 = temp_depth_ipv4;

        /* for debug, save the max depth reached */
        if (context->searcher_hash.hash_depth_reached < hash_entry->depth_ipv4 + hash_entry->num_ipv6) {
            context->searcher_hash.hash_depth_reached = hash_entry->depth_ipv4 + hash_entry->num_ipv6;
        }
        context->searcher_hash.num_tuples++;

        /* remeber the IP type and h_val to know where and how much
         * to decrease upon CID recycling */
        DbgBreakIf(context->array[cid].ip_type); /* cid can't be inserted twice */
        context->array[cid].ip_type = tuple->ip_type;
        context->array[cid].h_val = h_val;
    }

    /* release spinlock */
    MM_RELEASE_CID_LOCK(pdev);

    return lm_status;
}

/* assumption: CID lock NOT taken by caller */
void lm_searcher_mirror_hash_remove(struct _lm_device_t *pdev, u32_t cid)
{
    lm_context_info_t        *context    = NULL;
    lm_searcher_hash_entry_t *hash_entry = NULL;
    u32_t                    h_val       = 0;

    /* take spinlock */
    MM_ACQUIRE_CID_LOCK(pdev);

    context = pdev->context_info;

    if(!context->array[cid].ip_type) {
        /* i.e lm_searcher_mirror_hash_insert was not called for this cid */
        DbgMessage(pdev, WARN,
                   "not removing CID %d from SRC hash (hash insert was not called for this cid)\n"
                   ,cid);

        /* release spinlock */
        MM_RELEASE_CID_LOCK(pdev);

        return;
    }

    h_val = context->array[cid].h_val;
    hash_entry = &context->searcher_hash.searcher_table[h_val];

    if (context->array[cid].ip_type == LM_IP_TYPE_V6) {
        DbgBreakIf(!hash_entry->num_ipv6);
        hash_entry->num_ipv6--;
    }
    else
    {
        DbgBreakIf(!hash_entry->num_ipv4);
        hash_entry->num_ipv4--;
        if (hash_entry->num_ipv4 < hash_entry->depth_ipv4)
        {
            hash_entry->depth_ipv4 = hash_entry->num_ipv4;
        }
    }

    /* for debug */
    context->searcher_hash.num_tuples--;

    /* clear the entry of the context */
    context->array[cid].ip_type = 0;
    context->array[cid].h_val = 0;

    /* release spinlock */
    MM_RELEASE_CID_LOCK(pdev);
}

/*  allocate a free context by type
    returns CID in the out_cid param
    return LM_STATUS_SUCCESS for available cid
    LM_STATUS_RESOURCE if no cids are available
    LM_STATUS_PENDING if there is a pending cfc-delete cid
    takes the list spinlock */
lm_status_t lm_allocate_cid(struct _lm_device_t *pdev, u32_t type, void * cookie, s32_t * out_cid){

    lm_context_info_t  *context  = NULL;
    lm_status_t        lm_status = LM_STATUS_SUCCESS;
    u32_t              cid       = (u32_t)-1;
    lm_address_t       phy_addr  = {{0}} ;

    if ( CHK_NULL(out_cid) ||
         CHK_NULL(pdev) ||
         CHK_NULL(pdev->context_info) ||
         CHK_NULL(pdev->context_info->array) ||
         CHK_NULL(cookie) ||
         ERR_IF(type >= ARRSIZE(pdev->context_info->proto_pending)) )

    {
        DbgBreakIf(!out_cid) ;
        DbgBreakIf(!pdev);
        DbgBreakIf(!pdev->context_info);
        DbgBreakIf(!pdev->context_info->array);
        DbgBreakIf(!cookie);
        DbgBreakIf(type >= ARRSIZE(pdev->context_info->proto_pending)) ;
        return LM_STATUS_INVALID_PARAMETER ;
    }

    context = pdev->context_info;
    *out_cid = 0;
    /* take spinlock */
    MM_ACQUIRE_CID_LOCK(pdev);

    // if the free list is empty return error
    if (context->proto_ffree[type]==0) {
        if ((pdev->params.cid_allocation_mode == LM_CID_ALLOC_REGULAR) || (context->proto_pending[type] == 0)) {
            // if the free list is empty AND the pending list is empty return error OR
            // the free list is empty and we're in the regular allocating mode
            lm_status = LM_STATUS_RESOURCE;
        }
        else
        {
            /* pop pendinglist entry and place cookie */
            /* we only use the cid to connect between the pending connection and this cid, but
             * the connection can't know of this cid before it is acually freed, for this reason
             * we return cid = 0, which means, 'pending' */
            cid = context->proto_pending[type];
            context->proto_pending[type] = context->array[cid].next;
            context->array[cid].next = 0;
            context->array[cid].cid_resc.cookies[type] = cookie;
            context->array[cid].cid_resc.cid_pending = LM_CID_STATE_PENDING;
            lm_sp_req_manager_init(pdev, cid);
            *out_cid = cid;

            /* make sure the first cid previous is set correctly*/
            cid = context->proto_pending[type];
            if (cid) {
                context->array[cid].prev = 0;
            }
            lm_status = LM_STATUS_PENDING;
        }
    }else{
        /* pop freelist entry and place cookie*/
        cid = context->proto_ffree[type];
        context->proto_ffree[type] = context->array[cid].next;
        context->array[cid].next = 0;
        context->array[cid].prev = 0;
        context->array[cid].cid_resc.cookies[type] = cookie;
        lm_sp_req_manager_init(pdev, cid);
        *out_cid = cid;
        lm_status = LM_STATUS_SUCCESS;
    }

    MM_RELEASE_CID_LOCK(pdev);

    if(LM_STATUS_SUCCESS == lm_status)
    {
        //If the function allocated a new free CID, (not pending) the function MmMapIoSpace will be called
        //to map the specific physical cid doorbell space to a virtual address.
        //In case of a pending CID, the map doorbell space will not be remapped. The pending CID will use
        //the old mapping cid doorbell space.
        phy_addr.as_u32.low = (pdev->hw_info.mem_base[BAR_1].as_u32.low) & 0xfffffff0;
        phy_addr.as_u32.high = pdev->hw_info.mem_base[BAR_1].as_u32.high;

        LM_INC64(&phy_addr,(cid*LM_DQ_CID_SIZE));

#ifdef __SunOS

        context->array[cid].cid_resc.mapped_cid_bar_addr =
#ifdef VF_INVOLVED
            (volatile void *)((u8_t*)pdev->vars.mapped_bar_addr[BAR_1] + cid*LM_DQ_CID_SIZE);
        context->array[cid].cid_resc.reg_handle = pdev->vars.reg_handle[BAR_1];
#else /* !VF_INVOLVED */
            (volatile void *)mm_map_io_space_solaris(pdev,
                                                     phy_addr,
                                                     BAR_1,
                                                     (cid * LM_DQ_CID_SIZE),
                                                     LM_DQ_CID_SIZE,
                                                     &context->array[cid].cid_resc.reg_handle);
#endif /* VF_INVOLVED */

#else /* !__SunOS */

        context->array[cid].cid_resc.mapped_cid_bar_addr =
#ifdef VF_INVOLVED
            (volatile void *)((u8_t*)pdev->vars.mapped_bar_addr[BAR_1] + cid*LM_DQ_CID_SIZE);
#else /* !VF_INVOLVED */
            (volatile void *)mm_map_io_space(pdev, phy_addr, LM_DQ_CID_SIZE);
#endif /* VF_INVOLVED */

#endif /* __SunOS */

        // If the mapping failed we will return LM_STATUS_RESOURCE and return the cid resource.
        if CHK_NULL(context->array[cid].cid_resc.mapped_cid_bar_addr)
        {
            DbgMessage(pdev, FATAL, "lm_allocate_cid: mm_map_io_space failed. address low=%d address high=%d\n", phy_addr.as_u32.low,phy_addr.as_u32.high );

            /* take spinlock */
            MM_ACQUIRE_CID_LOCK(pdev);
            /* return the cid to free list */
            context->array[cid].next = pdev->context_info->proto_ffree[type];
            context->proto_ffree[type] = cid;
            context->array[cid].invalid = LM_CONTEXT_VALID;
            MM_RELEASE_CID_LOCK(pdev);

            lm_status = LM_STATUS_RESOURCE;
            *out_cid =0;
        }
    }
    return lm_status;
}

void lm_cfc_delete(struct _lm_device_t *pdev, void *param)
{
    u32_t cid             = (u32_t)(uintptr_t)param;
    u8_t  flr_in_progress = lm_fl_reset_is_inprogress(pdev);

    if ( CHK_NULL(pdev) ||
         ERR_IF(cid > pdev->params.max_func_connections) ||
         ERR_IF(pdev->context_info->array[cid].invalid != LM_CONTEXT_INVALID_WAIT) )
    {
        DbgBreakIf(!pdev);
        DbgBreakIf(cid > pdev->params.max_func_connections);

        if (!flr_in_progress)
        {
            DbgBreakIf(pdev->context_info->array[cid].invalid != LM_CONTEXT_INVALID_WAIT);
        }
        else
        {
            DbgMessage(pdev, FATAL, "lm_cfc_delete: invalid %d for cid=%d\n", pdev->context_info->array[cid].invalid,cid);

            if (pdev->context_info->array[cid].invalid != LM_CONTEXT_INVALID_DELETE)
            {
                DbgBreakIf(1);
            }
        }
    }

    DbgMessage(pdev, WARN, "lm_cfc_delete: cid=0x%x\n",cid);
    pdev->context_info->array[cid].invalid = LM_CONTEXT_INVALID_DELETE;

    if (lm_fl_reset_is_inprogress(pdev))
    {
         lm_recycle_cid(pdev, cid);
    }
    else
    {
        /* use common bit */
        lm_command_post(pdev,
                        cid,
                        RAMROD_CMD_ID_COMMON_CFC_DEL,
                        CMD_PRIORITY_NORMAL,
                        NONE_CONNECTION_TYPE,
                        0 );
    }
    return;
}

/* free a context
   takes the list spinlock */
void lm_free_cid(struct _lm_device_t *pdev, u32_t type, u32_t cid, u8_t notify_fw){
    u32_t delay_time  = 0;
    u32_t curr_cid    = 0;
    u8_t  recycle_now = 0;
    u8_t  proto_idx   = 0;

    if ( CHK_NULL(pdev) ||
         CHK_NULL(pdev->context_info) ||
         ERR_IF(type >= ARRSIZE(pdev->context_info->proto_end)) ||
         ERR_IF(cid > (pdev->context_info->proto_end[type])) ||
         ERR_IF(cid < (pdev->context_info->proto_start[type])) ||
         (!lm_fl_reset_is_inprogress(pdev) && (pdev->context_info->array[cid].invalid != LM_CONTEXT_VALID)))
    {
        DbgBreakIf(!pdev);
        DbgBreakIf(!pdev->context_info);
        DbgBreakIf(type >= ARRSIZE(pdev->context_info->proto_end));
        DbgBreakIf(cid > (pdev->context_info->proto_end[type]));
        DbgBreakIf(cid < (pdev->context_info->proto_start[type]));
        DbgBreakIf(pdev->context_info->array[cid].invalid != LM_CONTEXT_VALID);
        return;
    }
    MM_ACQUIRE_CID_LOCK(pdev);

    for (proto_idx = 0; proto_idx < MAX_PROTO; proto_idx++)
    {
        DbgBreakIf(pdev->context_info->array[cid].cid_resc.cookies[proto_idx]);
    }

    lm_sp_req_manager_shutdown(pdev, cid);

    if (notify_fw)
    {
        /* Vladz: Added in order to optimize CID release in DOS */
#if !(defined(DOS) || defined(__LINUX))
        delay_time = LM_FREE_CID_DELAY_TIME(pdev);
#else
        delay_time = 0;
#endif

        pdev->context_info->array[cid].invalid = LM_CONTEXT_INVALID_WAIT;

        recycle_now = FALSE;
        /* add the cid to proto-pending: it'll be freed soon when cfc-delete is done */
        curr_cid = pdev->context_info->proto_pending[type];
        pdev->context_info->array[cid].next = curr_cid;
        pdev->context_info->array[cid].prev = 0;
        if (curr_cid != 0)
        {
            pdev->context_info->array[curr_cid].prev = cid;
        }
        pdev->context_info->proto_pending[type] = cid;
    }
    else
    {
        pdev->context_info->array[cid].invalid = LM_CONTEXT_INVALID_DELETE;
        recycle_now = TRUE;
        /* If we're recylcing now, there's no point in adding it to the pending list */
    }

    MM_RELEASE_CID_LOCK(pdev);

    if (recycle_now) {
        lm_recycle_cid(pdev, cid);
    }
    else
    {
        if (type == TOE_CONNECTION_TYPE)
        {
            DbgMessage(pdev, WARN, "lm_free_cid: CFC delete: cid=0x%x\n",cid);
            lm_cfc_delete(pdev,(void *)(uintptr_t)cid);
        }
        else
        {
            DbgMessage(pdev, WARN, "lm_free_cid: schedule CFC delete: cid=0x%x\n",cid);
            mm_schedule_task(pdev,delay_time,lm_cfc_delete,
                (void *)(uintptr_t)cid);
        }
    }

}

void lm_recycle_cid(struct _lm_device_t *pdev, u32_t cid){

    u32_t type = MAX_PROTO+1;
    u32_t prev_cid, next_cid;
    u32_t i;
    u8_t  call_cb = TRUE;

    if ( CHK_NULL(pdev) ||
         ERR_IF(pdev->context_info->array[cid].invalid != LM_CONTEXT_INVALID_DELETE) ||
         ERR_IF(cid > pdev->params.max_func_connections) )
    {
        DbgBreakIf(!pdev);
        DbgBreakIf(pdev->context_info->array[cid].invalid != LM_CONTEXT_INVALID_DELETE);
        DbgBreakIf(cid > pdev->params.max_func_connections);
        return;
    }

    for (i=0; i < MAX_PROTO; i++ ) {
        if ((cid >= pdev->context_info->proto_start[i]) && (cid <= pdev->context_info->proto_end[i]))
        {
            type = i;
            break;
        }
    }
    if ERR_IF(type >= ARRSIZE(pdev->context_info->proto_pending))
    {
        DbgBreakIf(type >= ARRSIZE(pdev->context_info->proto_pending)) ;
        return;
    }
    /* take spinlock */
    MM_ACQUIRE_CID_LOCK(pdev);
#ifdef _VBD_
    if ((type == TOE_CONNECTION_TYPE) && (pdev->ofld_info.l4_params.ticks_per_second != 0))
    {
        pdev->vars.last_recycling_timestamp = mm_get_current_time(pdev) * 1000 / pdev->ofld_info.l4_params.ticks_per_second; /*time in ms*/
    }
#endif
    /* If no cookie is waiting on this cid extract from pending and push enrty into the freelist */
    if (pdev->context_info->array[cid].cid_resc.cid_pending == FALSE) {
        /* take the cid out of the proto_pending cids if it's there */
        prev_cid = pdev->context_info->array[cid].prev;
        next_cid = pdev->context_info->array[cid].next;
        if (prev_cid) {
            pdev->context_info->array[prev_cid].next = next_cid;
        }
        if (next_cid) {
            pdev->context_info->array[next_cid].prev = prev_cid;
        }
        if (pdev->context_info->proto_pending[type] == cid) {
            DbgBreakIf(prev_cid != 0);
            pdev->context_info->proto_pending[type] = next_cid;
        }
        pdev->context_info->array[cid].prev = pdev->context_info->array[cid].next = 0;
        /* add to free list */
        pdev->context_info->array[cid].next = pdev->context_info->proto_ffree[type];
        pdev->context_info->array[cid].invalid = LM_CONTEXT_VALID;
        pdev->context_info->array[cid].cfc_delete_cnt = 0;
        pdev->context_info->proto_ffree[type] = cid;
        call_cb = FALSE; /* no one is waiting on this... */
        //free virtual memory for cids not in use.
#ifndef VF_INVOLVED
        mm_unmap_io_space(pdev,(void *)pdev->context_info->array[cid].cid_resc.mapped_cid_bar_addr, LM_DQ_CID_SIZE);
#endif
    }
    else
    {
        /* No need to extract from pending - it's not there. */

        /* NirV: we still can't set cid_resc.cid_pending to false, */
        /* will be possible only in the callback */

        pdev->context_info->array[cid].invalid = LM_CONTEXT_VALID;
        call_cb = TRUE;
    }

    /* time to clear the active bit (cdu-validation ) we can only do this after cfc-delete has completed, at this point, invalid==LM_CONTEXT_VALID */
    lm_set_cdu_validation_data(pdev, cid, TRUE /* Invalidate */);


    /* rlease spinlock */
    MM_RELEASE_CID_LOCK(pdev);

    /* call here the cid recycle callback of that
       protocol type if such cb exists*/
    if (pdev->cid_recycled_callbacks[type] && call_cb) {
        pdev->cid_recycled_callbacks[type](pdev, pdev->context_info->array[cid].cid_resc.cookies[type], cid);
    }

    return;
}

/* lookup the protocol cookie for a given CID
   does not take a lock
   will DbgBreakIf( if the CID is not allocated. */
void * lm_cid_cookie(struct _lm_device_t *pdev, u32_t type, u32_t cid){

    if ( CHK_NULL(pdev) ||
         CHK_NULL(pdev->context_info) ||
         ERR_IF(type >= MAX_PROTO) ||
         ERR_IF(cid > (pdev->context_info->proto_end[MAX_PROTO - 1])) ||
         CHK_NULL(pdev->context_info->array[cid].cid_resc.cookies[type]) ||
         ERR_IF(pdev->context_info->array[cid].invalid != LM_CONTEXT_VALID) )
    {
        DbgBreakIf(!pdev);
        DbgBreakIf(!pdev->context_info);
        DbgBreakIf(type >= MAX_PROTO);
        DbgBreakIf(cid > (pdev->context_info->proto_end[MAX_PROTO - 1]));
        DbgBreakIf(pdev->context_info->array[cid].invalid != LM_CONTEXT_VALID);
    }

    if (pdev->context_info->array[cid].cid_resc.cookies[type] == NULL)
    {
        return NULL;
    }


    /* if the cid is pending, return null */
    if (pdev->context_info->array[cid].cid_resc.cid_pending != LM_CID_STATE_VALID)
    {
        return NULL;
    }

    return pdev->context_info->array[cid].cid_resc.cookies[type];
}

/* lookup the protocol cid_resc for a given CID
   does not take a lock
   will DbgBreakIf( if the CID is not allocated */
lm_cid_resc_t * lm_cid_resc(struct _lm_device_t *pdev, u32_t cid){

    if ( CHK_NULL(pdev) ||
         CHK_NULL(pdev->context_info) ||
         ERR_IF(cid > (pdev->context_info->proto_end[MAX_PROTO - 1])) )
    {
        DbgBreakIf(!pdev);
        DbgBreakIf(!pdev->context_info);
        DbgBreakIf(cid > (pdev->context_info->proto_end[MAX_PROTO - 1]));
    }

    return &pdev->context_info->array[cid].cid_resc;
}

u8_t lm_map_cid_to_proto(struct _lm_device_t * pdev, u32_t cid)
{
    u8_t type = MAX_PROTO+1;
    u8_t i;

    if (!pdev || cid > pdev->params.max_func_connections) {
        return type;
    }

    for (i=0; i < MAX_PROTO; i++ ) {
        if ((cid >= pdev->context_info->proto_start[i]) && (cid <= pdev->context_info->proto_end[i]))  {
            type = i;
            break;
        }
    }
    return type;
}

void lm_init_connection_context(struct _lm_device_t *pdev, u32_t const sw_cid, u8_t sb_id)
{
    struct eth_context * context      = NULL;

    if ( CHK_NULL(pdev) ||
         ERR_IF(sw_cid < PFDEV(pdev)->context_info->proto_start[ETH_CONNECTION_TYPE]) ||
         ERR_IF(sw_cid > PFDEV(pdev)->context_info->proto_end[ETH_CONNECTION_TYPE]) )
    {
        DbgBreakIf(!pdev);
        DbgBreakIf(sw_cid < PFDEV(pdev)->context_info->proto_start[ETH_CONNECTION_TYPE]); /* first legal NIC CID */
        DbgBreakIf(sw_cid > PFDEV(pdev)->context_info->proto_end[ETH_CONNECTION_TYPE]);   /* last legal NIC CID */
    }

    context = lm_get_context(PFDEV(pdev), VF_TO_PF_CID(pdev,sw_cid));

    mm_mem_zero( context, sizeof(struct eth_context) ) ;

    /* calculate the cdu-validation value. */
    lm_set_cdu_validation_data(pdev, VF_TO_PF_CID(pdev,sw_cid), FALSE /* don't invalidate */);

}

lm_status_t
lm_set_cid_resc(
    IN struct _lm_device_t *pdev,
    IN u32_t type,
    IN void *cookie,
    IN u32_t cid)
{
    lm_status_t     lm_status  = LM_STATUS_SUCCESS;
    lm_cid_resc_t   *cid_resc  = NULL;

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    /* take spinlock */
    MM_ACQUIRE_CID_LOCK(pdev);

    cid_resc = lm_cid_resc(pdev, cid);

    if CHK_NULL(cid_resc)
    {
        MM_RELEASE_CID_LOCK(pdev);
        return LM_STATUS_INVALID_PARAMETER;
    }

    cid_resc->cookies[type] = cookie;

    /* rlease spinlock */
    MM_RELEASE_CID_LOCK(pdev);

    return lm_status;
}

lm_status_t
lm_free_cid_resc(
    IN    struct _lm_device_t *pdev,
    IN    u32_t type,
    IN    u32_t cid,
    IN    u8_t notify_fw)
{
    lm_cid_resc_t   *cid_resc = NULL;
    u8_t            proto_idx = 0;


    if (CHK_NULL(pdev) || (cid == 0))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    /* take spinlock */
    MM_ACQUIRE_CID_LOCK(pdev);

    cid_resc = lm_cid_resc(pdev, cid);

    if CHK_NULL(cid_resc)
    {
        MM_RELEASE_CID_LOCK(pdev);
        return LM_STATUS_INVALID_PARAMETER;
    }

    cid_resc->cookies[type] = NULL;

    while ((proto_idx < MAX_PROTO) && (cid_resc->cookies[proto_idx] == NULL))
    {
        proto_idx++;
    }
    /* rlease spinlock */
    MM_RELEASE_CID_LOCK(pdev);

    if (proto_idx == MAX_PROTO)
    {
        /* We'll call lm_map_cid_to_proto() to compute the appropriate type that was associated with that CID,
         * this is done to avoid assert upon race scenarios in which the last cookie resource that gets freed is not from the type of the CID */
        lm_free_cid(pdev, lm_map_cid_to_proto(pdev, cid), cid, notify_fw);
    }

    return LM_STATUS_SUCCESS;
}



lm_sp_req_manager_t *
lm_cid_sp_req_mgr(
    IN struct _lm_device_t *pdev,
    IN u32_t cid
    )
{
    lm_cid_resc_t   *cid_resc   = NULL;

    if CHK_NULL(pdev)
    {
        return NULL;
    }

    cid_resc = lm_cid_resc(pdev, cid);

    if CHK_NULL(cid_resc)
    {
        return NULL;
    }

    return &cid_resc->sp_req_mgr;
}



lm_cid_state_enum
lm_cid_state(
    IN struct _lm_device_t *pdev,
    IN u32_t cid
    )
{
    lm_cid_resc_t   *cid_resc   = NULL;

    if CHK_NULL(pdev)
    {
        return LM_CID_STATE_ERROR;
    }

    cid_resc = lm_cid_resc(pdev, cid);

    if CHK_NULL(cid_resc)
    {
        return LM_CID_STATE_ERROR;
    }

    return (lm_cid_state_enum)cid_resc->cid_pending;
}



lm_status_t
lm_set_cid_state(
    IN struct _lm_device_t *pdev,
    IN u32_t cid,
    IN lm_cid_state_enum state
    )
{
    lm_cid_resc_t   *cid_resc   = NULL;

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    /* take spinlock */
    MM_ACQUIRE_CID_LOCK(pdev);

    cid_resc = lm_cid_resc(pdev, cid);

    if CHK_NULL(cid_resc)
    {
        MM_RELEASE_CID_LOCK(pdev);
        return LM_STATUS_INVALID_PARAMETER;
    }

    cid_resc->cid_pending = state;

    /* rlease spinlock */
    MM_RELEASE_CID_LOCK(pdev);

    return LM_STATUS_SUCCESS;
}

/**
 * sets the CDU validation data to be valid for a given cid
 *
 * @param pdev - the physical device handle
 * @param cid - the context of this cid will be initialized with the cdu validataion data
 *
 * @return lm_status_t
 */
lm_status_t lm_set_cdu_validation_data(struct _lm_device_t *pdev, s32_t cid, u8_t invalidate)
{
    lm_status_t lm_status = LM_STATUS_SUCCESS;
    void        *context        = NULL;
    u8_t        *cdu_reserved   = NULL; /* Pointer to the actual location of cdu_reserved field according to protocol */
    u8_t        *cdu_usage      = NULL; /* Pointer to the actual location of cdu_usage field according to protocol */
    u8_t        proto_type      = 0;

    context = lm_get_context(PFDEV(pdev), cid);

    if (!context) {
        return LM_STATUS_FAILURE;
    }

    proto_type = lm_map_cid_to_proto(PFDEV(pdev), cid);

    switch (proto_type) {
    case TOE_CONNECTION_TYPE:
        cdu_reserved = &((struct toe_context *)context)->xstorm_ag_context.cdu_reserved;
        cdu_usage = &(((struct toe_context *)context)->ustorm_ag_context.cdu_usage);
        break;
    case ETH_CONNECTION_TYPE:
        cdu_reserved = &(((struct eth_context *)context)->xstorm_ag_context.cdu_reserved);
        cdu_usage =  &(((struct eth_context *)context)->ustorm_ag_context.cdu_usage);
        break;
    case ISCSI_CONNECTION_TYPE:
        cdu_reserved = &(((struct iscsi_context *)context)->xstorm_ag_context.cdu_reserved);
        cdu_usage = &(((struct iscsi_context *)context)->ustorm_ag_context.cdu_usage);
        break;
    case FCOE_CONNECTION_TYPE:
        cdu_reserved = &(((struct fcoe_context *)context)->xstorm_ag_context.cdu_reserved);
        cdu_usage = &(((struct fcoe_context *)context)->ustorm_ag_context.cdu_usage);
        break;
    default:
        lm_status = LM_STATUS_FAILURE;
        break;
    }

    if (cdu_reserved && cdu_usage) {
        if (invalidate) {
            *cdu_reserved = CDU_RSRVD_INVALIDATE_CONTEXT_VALUE(*cdu_reserved);
            *cdu_usage    = CDU_RSRVD_INVALIDATE_CONTEXT_VALUE(*cdu_usage);
        } else {
            *cdu_reserved = CDU_RSRVD_VALUE_TYPE_A(HW_CID(pdev, cid), CDU_REGION_NUMBER_XCM_AG, proto_type);
            *cdu_usage    = CDU_RSRVD_VALUE_TYPE_A(HW_CID(pdev, cid), CDU_REGION_NUMBER_UCM_AG, proto_type);
        }
    }

    return lm_status;
}


lm_status_t lm_get_context_size(struct _lm_device_t *pdev, s32_t * context_size)
{
    *context_size = LM_CONTEXT_SIZE;
    return LM_STATUS_SUCCESS;
}

lm_status_t lm_set_con_state(struct _lm_device_t *pdev, u32_t cid, u32_t state)
{
    lm_cid_resc_t * cid_resc = lm_cid_resc(pdev, cid);

    if CHK_NULL(cid_resc)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    cid_resc->con_state = state;

    return LM_STATUS_SUCCESS;
}

u32_t lm_get_con_state(struct _lm_device_t *pdev, u32_t cid)
{
    const lm_cid_resc_t * cid_resc = lm_cid_resc(pdev, cid);

    if CHK_NULL(cid_resc)
    {
        return LM_CON_STATE_CLOSE;
    }

    return cid_resc->con_state;
}