/*******************************************************************************
*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2014 QLogic Corporation
* The contents of this file are subject to the terms of the
* QLogic End User License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License at
* See the License for the specific language governing permissions
* and limitations under the License.
*
* Module Description:
*
*
* History:
* 11/15/01 Hav Khauv Inception.
* 4/4/06 Eliezer begin modifying
******************************************************************************/
#include "lm5710.h"
#include "microcode_constants.h"
#include "eth_constants.h"
#include "bd_chain.h"
#include "ecore_common.h"
{
//the hw_con_idx_ptr of the rcq_chain points directly to the Rx index in the USTORM part of the non-default status block
//changed from *tx_chain->hw_con_idx_ptr != tx_chain->cons_idx
{
}
return result;
}
)
{
//increase nbd on account of the split BD
//fix the num of bytes of the BD which has the headers+data to correspond only to the headers part
//this is phys addr which points to the start of the data part right after the end of the headers
//Advance to the next BD.
//fill the fields of the new additional BD which holds _only_ data
DbgMessage(NULL, WARNl2tx, "#lm_handle_lso_split: after split: original bd nbytes=0x%x,new bd nbytes=0x%x\n",
}
static void lm_pre_process_lso_packet(
)
{
/* find headers nbds, for that calc eth_hlen and total_hlen_bytes,
and take the opportunity to decide if header data separation is required */
*split_required = FALSE;
{
hdr_nbds++;
if (total_hlen_bytes <= sum_frag_size)
{
if (total_hlen_bytes < sum_frag_size)
{
*split_required = TRUE;
}
break;
}
}
}
{
/* "Sanity check. Maximum total length for IP and TCP headers
* is 120 bytes." was here. The sanity check is removed. Corresponding statistics is added */
}
if (CHIP_IS_E1x(pdev))
{
//in case of LSO it is required according to fw to toggle the ETH_TX_PARSE_BD_PSEUDO_CS_WITHOUT_LEN flag since the TCP seg len is 0
{
}
}
else
{
parse_bd_e2->parsing_data |= ETH_TX_PARSE_BD_E2_LSO_MSS & (packet->l2pkt_tx_info->lso_mss << ETH_TX_PARSE_BD_E2_LSO_MSS_SHIFT);
}
//enforce this due to miniport design in case of LSO and CSUM
{
}
//required only in case of LSO - num of bds the headers occupy all together
start_bd->general_data |= ((packet->u1.tx.hdr_nbds & ETH_TX_START_BD_HDR_NBDS) << ETH_TX_START_BD_HDR_NBDS_SHIFT);
//check for split in START BD
if (split_required)
{
{
}
else
{
}
}
{
//Advance to the next BD.
//if there is a split condition and we are on the exact BD, do it! we don't enter here if there was a split already!
if (split_required)
{
{
);
}
else
{
}
}
(*frag)++;
}
//statistics
//since this is fast path, we do not use ATOMIC INC.
//therefore the statistic might not be completely accurate
}
/**
* @Description:
* returns coalesce buffer of size >= buf_size, or NULL if none available
* @Assumptions:
* txq lock is taken by the caller
*/
{
return NULL;
}
{
&txq->coalesce_buf_list);
if(NULL == coalesce_buf)
{
//this case were coalesce buffer in the list is equal to null shouldn't happen.
break;
}
{
txq->coalesce_buf_used++;
break;
}
coalesce_buf = NULL;
}
return coalesce_buf;
} /* lm_get_coalesce_buffer */
/**
* @Description:
* returns coalesce_buf into txq list
* @Assumptions:
* txq lock is taken by the caller
*/
void
{
return;
}
return;
} /* lm_put_coalesce_buffer */
/**
* @Description:
* copy given packet into available coalesce buffer of given txq
* @Assumptions:
* txq lock is taken by the caller
* @Returns:
* - SUCCESS -
* - The OUT parameter coal_buf will be set to point the allocated
* coalesce buffer
* - The coalesce buffer frag size will be set to the given packet size
* - RESOURCE - no available coalecse buffer for given packet
* (according to packet size)
*/
static lm_status_t
)
{
{
return LM_STATUS_FAILURE;
}
/* Determine packet size. */
}
/* Find a buffer large enough for copying this packet. In the case
* of an LSO frame, we should have at least one 64k coalesce buffer. */
if(coalesce_buf == NULL)
{
"#copy to coalesce buffer FAILED, (lmpkt=0x%p,pkt_size=%d)\n",
return LM_STATUS_RESOURCE;
}
/* copy the packet into the coalesce buffer */
return LM_STATUS_FAILURE;
}
/* adjust frag size in coalesce buf */
*coal_buf = coalesce_buf;
return LM_STATUS_SUCCESS;
} /* lm_copy_packet_to_coalesce_buffer */
/**
* @Description:
* check if packet requires copying to coalesce buf (packet too fregmented)
* @Returns:
* TRUE or FALSE
*/
static u8_t
)
{
// each window size consective TCP payload BDs, must hold payload size
// which is greater than, or equal to MSS size.
{
{
/* Too fragmented LSO packet, check if it needs to be copied: */
{
{
}
{
"#copy to coalesce buffer IS REQUIRED for LSO packet, (lmpkt=0x%p,num_frags=%d)\n",
break;
}
wnd_sum = 0;
}
}
else
{
/* in non LSO, too fragmented packet should always
be copied to coalesce buffer */
"#copy to coalesce buffer IS REQUIRED for NON LSO packet, (lmpkt=0x%p,num_frags=%d)\n",
}
}
return to_copy;
} /* lm_is_packet_coalescing_required */
/**
* @description
* Check if VLAN exist and if the VLAN exists get priority.
* @param pdev
* @param packet
*
* @return u32_t
*/
{
//untagged packets should be treated as priority 0
if GET_FLAGS(packet->l2pkt_tx_info->flags , (LM_TX_FLAG_INSERT_VLAN_TAG | LM_TX_FLAG_VLAN_TAG_EXISTS))
{
}
return pri;
}
void
struct eth_tunnel_data *tunnel_data,
struct eth_tx_parse_2nd_bd *parse_bd_2nd_ptr,
{
// Inner IP header offset in WORDs (16-bit) from start of packet
// Checksum of pseudo header with length field = 0
// Outer ip header checksum (with ALL ip header fields) for non-lso encaulated packet
{
// Set in case outer IP header is ipV6
}
if (!parse_bd_2nd_ptr)
{
return;
}
// Outer IP header offset in WORDs (16-bit) from start of packet
parse_bd_2nd_ptr->global_data |= ( ((eth_hlen) >> 1) << ETH_TX_PARSE_2ND_BD_IP_HDR_START_OUTER_W_SHIFT);
{
// Outer ipV4 header length in words
parse_bd_2nd_ptr->global_data |= ( ((packet->l2pkt_tx_info->lso_ip_hdr_len) >> 1) << ETH_TX_PARSE_2ND_BD_IP_HDR_LEN_OUTER_W_SHIFT);
}
// An optional addition to ECN that protects against accidental or malicious concealment of marked packets from the TCP sender
parse_bd_2nd_ptr->global_data |= (packet->l2pkt_tx_info->tcp_nonce_sum_bit << ETH_TX_PARSE_2ND_BD_NS_FLG_SHIFT);
// Checksum of pseudo header with length field=0
parse_bd_2nd_ptr->tcp_flags = packet->l2pkt_tx_info->lso_tcp_flags; // no endianity since it is u8_t
/* We set tunnel_lso_inc_ip_id as constant, INT_HEADER, so the "HW IP header" is the inner header.
Assuming "FW IP header" is the outer IP header, and "HW IP header" is the inner IP header:
fw_ip_csum_wo_len_flags_frag - is the IP checksum without length, flags and fragment offset of the outer ip header
hw_ip_id - is the ip id of the inner ip id */
parse_bd_2nd_ptr->fw_ip_hdr_to_payload_w = (packet->l2pkt_tx_info->encap_packet_inner_frame_offset +
eth_hlen) >> 1;
{
/* In IpV4, the length (in WORDs) from the FW IpV4 header start to the payload start. In IpV6, the length (in WORDs) from the FW IpV6 header end to the payload start. However, if extension headers are included, their length is counted here as well. */;
// if the outer header (fw header) is ipv4 than fw_ip_hdr_to_payload_w will be set to:
// the length in words from start of outer IP header to start of payload
// = outer ip header + gre header + inner mac header + inner ip header + tcp header length
//
// If the outer header is ipv6 than fw_ip_hdr_to_payload_w will be set to:
// the length in words from end of inner IP header to start of payload + extension headers (if exists)
// = outer ip header - fixed ip header + gre header + inner mac header + inner ip header + tcp header length
// fixed ipv6 header length is 40 bytes = 20 words
}
}
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
#if defined(__BIG_ENDIAN)
#elif defined(__LITTLE_ENDIAN)
#endif
//DbgBreakIfFastPath(chain_idx >= pdev->params.rss_chain_cnt);
// Compute Ethernet Header Len
{
}
{
}
GET_FLAGS(packet->l2pkt_tx_info->flags, (LM_TX_FLAG_COMPUTE_IP_CKSUM | LM_TX_FLAG_COMPUTE_TCP_UDP_CKSUM | LM_TX_FLAG_TCP_LSO_FRAME)));
{
{
// only for encapsulated packet with lso offload we need second parsing bd
num_parsing_bds = 2;
}
// encapsulated packet header size includes both outer and inner headers
}
else
{
//calculate the total sum of ETH + IP + TCP headers in term of bytes
total_hlen_bytes = packet->l2pkt_tx_info->lso_ip_hdr_len + packet->l2pkt_tx_info->lso_tcp_hdr_len + eth_hlen;
}
{
}
/* handle packet coalescing - if required, copy the too fregmented packet
into a pre-allocated coalesce buffer */
{
{
/* pkt coal buf can't already be set */
return LM_STATUS_FAILURE;
}
if (lm_status == LM_STATUS_SUCCESS)
{
split_required = 1;
/* from here on, use the coalesce buf frags list
instead of the frags list given by the caller */
}
else
{
return lm_status; /* no coalesce buf available, can't continue */
}
}
// stringent heuristic - number of parsing bds + a split of hdr & data
{
{
/* TODO: change this to "goto out_err:" */
}
return LM_STATUS_RESOURCE;
}
//initialize the start BD
// set the number of parsing BDs in packet.
// parse_nbds is set to: the number of parsing BDs in packet - 1
{
// tunnel_exist should be set iff the packet is encapsulated
// for encapsulated packets ETH_TX_BD_FLAGS_IPV6 refers to the inner header
{
}
}
else
{
// set in case ipV6 packet
{
}
}
{
start_bd->bd_flags.as_bitfield |= (ETH_TX_BD_FLAGS_VLAN_MODE & (X_ETH_OUTBAND_VLAN << ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT));
}
{
start_bd->bd_flags.as_bitfield |= (ETH_TX_BD_FLAGS_VLAN_MODE & (X_ETH_INBAND_VLAN << ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT));
}
else
{
if (vlan_tag == VLAN_TAGGED_FRAME_ETH_TYPE) {
start_bd->bd_flags.as_bitfield |= (ETH_TX_BD_FLAGS_VLAN_MODE & (X_ETH_INBAND_VLAN << ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT));
}
} else {
}
}
{
//force vlan mode according to bds (vlan mode can change accroding to global configuration)
}
frag++;
//SNAP
//parse bd is always present for FW simplicity
//adjust the parse BD pointer
/////////////////start parse BD handling ////////////////////////////////////////////
if (CHIP_IS_E1x(pdev))
{
}
else
{
}
// first parse BD taken into account
start_bd_nbd++;
if (num_parsing_bds > 1)
{
// lso offload for encapsulated packet - two parsing bds are required
//second parse BD taken into account
start_bd_nbd++;
}
{
fill_bds_for_encapsulated_packet(pdev, packet, &parse_bd_e2->data.tunnel_data, parse_bd_2nd_ptr, eth_hlen);
}
/////////////////end parse BD handling ////////////////////////////////////////////
{
}
{
{
}
}
else //This is the regular path in case we're not LSO
{
// In non-LSO packets, if there are more than 1 data bds, the second data bd (the one after
// the parsing bd) will be of the above type.total_pkt_bytes will hold the total packet length,
// without outer vlan and without vlan in case there is vlan offload.
//pass on all frags except the first one
{
// TODO: assert/ fixup if to many SGE's per MTU
//Advance to the next BD.
if (NULL == total_pkt_bytes_bd)
{
//second data bd saved for updating total_pkt_bytes.
}
frag++;
}
if (NULL != total_pkt_bytes_bd)
{
//we have a second data bd
}
}
//we might have IP csum, TCP csum, both or none.
//It is definitely legit for a packet to be csum offloaded with or without LSO!
//If the packet is LSO, we must enter here!!!!
if (GET_FLAGS(packet->l2pkt_tx_info->flags, (LM_TX_FLAG_COMPUTE_IP_CKSUM | LM_TX_FLAG_COMPUTE_TCP_UDP_CKSUM)))
{
// non-encapsulated packet: set bit if LM_TX_FLAG_COMPUTE_IP_CKSUM is on (LM_TX_FLAG_ENCAP_PACKET_IS_INNER_IPV6 is always equal to zero)
// encapsulated packet: set bit if LM_TX_FLAG_COMPUTE_IP_CKSUM is on and inner ip header is ipv4
{
}
{
{
}
}
if (CHIP_IS_E1x(pdev)) {
if (CHK_NULL(parse_bd_ptr)) {
return LM_STATUS_FAILURE ;
}
parse_bd_e1x->global_data |= (( (eth_hlen) >> 1) << ETH_TX_PARSE_BD_E1X_IP_HDR_START_OFFSET_W_SHIFT);
parse_bd_e1x->total_hlen_w = mm_cpu_to_le16((packet->l2pkt_tx_info->lso_ip_hdr_len >> 1) + ( (eth_hlen) >> 1));
}
{
parse_bd_e1x->global_data |= (packet->l2pkt_tx_info->tcp_nonce_sum_bit << ETH_TX_PARSE_BD_E1X_NS_FLG_SHIFT);
}
} else {
// TCP header Offset in WORDs from start of packet
{
/* set if the inner ip header is ipv6 with extension headers */
}
}
else
{
}
}
parse_bd_e2->parsing_data |= ETH_TX_PARSE_BD_E2_L4_HDR_START_OFFSET_W & (val << ETH_TX_PARSE_BD_E2_L4_HDR_START_OFFSET_W_SHIFT);
parse_bd_e2->parsing_data |= ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW & (val << ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW_SHIFT);
}
}
if ((!is_encapsulated_offload) &&
{
}
}
/* set dst addr type, if different from unicast */
{
{
}
else
{
}
if (CHIP_IS_E1x(pdev))
{
}
else
{
}
}
// Save the number of BDs used. Later we need to add this value back
// to tx_chain->bd_left when the packet is sent.
/* There is a PBF limitation on minimum packet size (9B)
* We assert since we do not expect packet length < 14 */
#if DBG
{
{
if (CHIP_IS_E1x(pdev))
{
" parse_bd: global_data 0x%x",
}
else /* E2 */
{
" parse_bd: parsing_data 0x%08x",
}
}
else
{
"-> frag: %d, bd_flags: %d, nbytes: %d, hi: 0x%x, lo: 0x%x",
if (cnt == 0)
{
" start bd info: nbds: %d, vlan: 0x%x, hdr_nbds: %d",
}
}
}
#endif
//in case of a packet consisting of 1 frag only, but with the use of parsing info BD,
//the last_bd will point to the START BD!
//this is since we need to mark both the START & END on the START BD.
//Only the start BD can fill the flags and we always have 2 BDs.
// Debug message on the parsed_bd
//DbgMessage(pdev, INFORM, "lm_send_packet() parse_bd: total_hlen %d ip_hlen %d lso_mss %d tcp_flags 0x%x\n",
// parse_bd->total_hlen, parse_bd->ip_hlen, parse_bd->lso_mss, parse_bd->tcp_flags);
//DbgMessage(pdev, INFORM, "lm_send_packet() start_bd: bd_flags 0x%x\n",start_bd->bd_flags);
// Make sure that the BD data is updated before updating the producer
// since FW might read the BD right after the producer is updated.
// This is only applicable for weak-ordered memory model archs such
// as IA-64, The following barrier is also mandatory since FW will
// assumes packets must have BDs
//order is crucial in case of preemption
pdev->tx_info.chain[chain_idx].eth_tx_prods.bds_prod = pdev->tx_info.chain[chain_idx].eth_tx_prods.bds_prod +
pdev->tx_info.chain[chain_idx].eth_tx_prods.packets_prod = pdev->tx_info.chain[chain_idx].eth_tx_prods.packets_prod + 1;
//DB
dq_msg.zero_fill1 = 0;
// Make sure that the BD data is updated before updating the producer
// since FW might read the BD right after the producer is updated.
// This is only applicable for weak-ordered memory model archs such
// as IA-64, The following barrier is also mandatory since FW will
// assumes packets must have BDs
//order is crucial in case of preemption
return LM_STATUS_SUCCESS;
} /* lm_send_packet */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
{
/* Get the new consumer idx. The bd's between new_idx and old_idx
* are bd's that have been consumered by the chip. */
//We work here with packets granularity(pkt_idx) as opposed to Teton which
//work in BDs granularity. the cons_idx is not relevant anymore in Tx chain, but
//we keep it for debugging as the firmware still maintains a BD consumer.
DbgBreakIfFastPath(pkt_num == 0);
while(pkt_num > 0)
{
//instead of the assert, lets check the db counter in the hw!
//DbgBreakIfFastPath(pkt == NULL);
{
return pkt_cnt;
}
// TODO check LSO condition as in teton
/* Advance the old_idx to the start bd_idx of the next packet. */
/* return coalesce buffer to the chain's pool */
}
/* Get an updated new_idx from the status block. The index may
* end at the last BD of a page. This BD is a pointer to the next
* BD page which we need to skip over. */
//TODO: need to verify that we have fairness among other protocols since we are also using the
// in_dpc_loop_cnt - so don't starve!
pkt_cnt++;
}
// TODO: currently bd_chain doesn't maintain the cons_idx...
DbgMessage(pdev, INFORMl2tx , "lm_get_packets_sent()- func: %d, txidx: %d, txbd con: %d txbd prod: %d \n",
FUNC_ID(pdev), chain_idx , lm_bd_chain_cons_idx(&tx_chain->bd_chain), lm_bd_chain_prod_idx(&tx_chain->bd_chain));
return pkt_cnt;
} /* lm_get_packets_sent */