#ifndef _ICE_COMMON_TXRX_H_
#define _ICE_COMMON_TXRX_H_
#include <netinet/udp.h>
#include <netinet/sctp.h>
static inline int
ice_tso_detect_sparse(if_pkt_info_t pi)
{
int count, curseg, i, hlen, segsz, seglen, tsolen, hdrs, maxsegs;
bus_dma_segment_t *segs = pi->ipi_segs;
int nsegs = pi->ipi_nsegs;
curseg = hdrs = 0;
hlen = pi->ipi_ehdrlen + pi->ipi_ip_hlen + pi->ipi_tcp_hlen;
tsolen = pi->ipi_len - hlen;
i = 0;
curseg = segs[0].ds_len;
while (hlen > 0) {
hdrs++;
if (hdrs > ICE_MAX_TSO_HDR_SEGS)
return (1);
if (curseg == 0) {
i++;
if (__predict_false(i == nsegs))
return (1);
curseg = segs[i].ds_len;
}
seglen = min(curseg, hlen);
curseg -= seglen;
hlen -= seglen;
}
maxsegs = ICE_MAX_TX_SEGS - hdrs;
if (nsegs <= maxsegs)
return (0);
count = 0;
while (tsolen > 0) {
segsz = pi->ipi_tso_segsz;
while (segsz > 0 && tsolen != 0) {
count++;
if (count > maxsegs) {
return (1);
}
if (curseg == 0) {
i++;
if (__predict_false(i == nsegs)) {
return (1);
}
curseg = segs[i].ds_len;
}
seglen = min(curseg, segsz);
segsz -= seglen;
curseg -= seglen;
tsolen -= seglen;
}
count = 0;
}
return (0);
}
static inline int
ice_tso_setup(struct ice_tx_queue *txq, if_pkt_info_t pi)
{
struct ice_tx_ctx_desc *txd;
u32 cmd, mss, type, tsolen;
int idx;
u64 type_cmd_tso_mss;
idx = pi->ipi_pidx;
txd = (struct ice_tx_ctx_desc *)&txq->tx_base[idx];
tsolen = pi->ipi_len - (pi->ipi_ehdrlen + pi->ipi_ip_hlen + pi->ipi_tcp_hlen);
type = ICE_TX_DESC_DTYPE_CTX;
cmd = ICE_TX_CTX_DESC_TSO;
if (pi->ipi_tso_segsz < ICE_MIN_TSO_MSS) {
txq->stats.mss_too_small++;
pi->ipi_tso_segsz = ICE_MIN_TSO_MSS;
}
mss = pi->ipi_tso_segsz;
type_cmd_tso_mss = ((u64)type << ICE_TXD_CTX_QW1_DTYPE_S) |
((u64)cmd << ICE_TXD_CTX_QW1_CMD_S) |
((u64)tsolen << ICE_TXD_CTX_QW1_TSO_LEN_S) |
((u64)mss << ICE_TXD_CTX_QW1_MSS_S);
txd->qw1 = htole64(type_cmd_tso_mss);
txd->tunneling_params = htole32(0);
txq->stats.tso++;
return ((idx + 1) & (txq->desc_count-1));
}
static inline void
ice_tx_setup_offload(struct ice_tx_queue *txq, if_pkt_info_t pi, u32 *cmd, u32 *off)
{
u32 remaining_csum_flags = pi->ipi_csum_flags;
switch (pi->ipi_etype) {
#ifdef INET
case ETHERTYPE_IP:
if (pi->ipi_csum_flags & ICE_CSUM_IP) {
*cmd |= ICE_TX_DESC_CMD_IIPT_IPV4_CSUM;
txq->stats.cso[ICE_CSO_STAT_TX_IP4]++;
remaining_csum_flags &= ~CSUM_IP;
} else
*cmd |= ICE_TX_DESC_CMD_IIPT_IPV4;
break;
#endif
#ifdef INET6
case ETHERTYPE_IPV6:
*cmd |= ICE_TX_DESC_CMD_IIPT_IPV6;
txq->stats.cso[ICE_CSO_STAT_TX_IP6]++;
break;
#endif
default:
txq->stats.cso[ICE_CSO_STAT_TX_L3_ERR]++;
break;
}
*off |= (pi->ipi_ehdrlen >> 1) << ICE_TX_DESC_LEN_MACLEN_S;
*off |= (pi->ipi_ip_hlen >> 2) << ICE_TX_DESC_LEN_IPLEN_S;
if (!(remaining_csum_flags & ~ICE_RX_CSUM_FLAGS))
return;
switch (pi->ipi_ipproto) {
case IPPROTO_TCP:
if (pi->ipi_csum_flags & ICE_CSUM_TCP) {
*cmd |= ICE_TX_DESC_CMD_L4T_EOFT_TCP;
*off |= (pi->ipi_tcp_hlen >> 2) <<
ICE_TX_DESC_LEN_L4_LEN_S;
txq->stats.cso[ICE_CSO_STAT_TX_TCP]++;
}
break;
case IPPROTO_UDP:
if (pi->ipi_csum_flags & ICE_CSUM_UDP) {
*cmd |= ICE_TX_DESC_CMD_L4T_EOFT_UDP;
*off |= (sizeof(struct udphdr) >> 2) <<
ICE_TX_DESC_LEN_L4_LEN_S;
txq->stats.cso[ICE_CSO_STAT_TX_UDP]++;
}
break;
case IPPROTO_SCTP:
if (pi->ipi_csum_flags & ICE_CSUM_SCTP) {
*cmd |= ICE_TX_DESC_CMD_L4T_EOFT_SCTP;
*off |= (sizeof(struct sctphdr) >> 2) <<
ICE_TX_DESC_LEN_L4_LEN_S;
txq->stats.cso[ICE_CSO_STAT_TX_SCTP]++;
}
break;
default:
txq->stats.cso[ICE_CSO_STAT_TX_L4_ERR]++;
break;
}
}
static void
ice_rx_checksum(struct ice_rx_queue *rxq, uint32_t *flags, uint32_t *data,
u16 status0, u16 ptype)
{
const u16 l3_error = (BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_IPE_S) |
BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S));
const u16 l4_error = (BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_L4E_S) |
BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_EUDPE_S));
const u16 xsum_errors = (l3_error | l4_error |
BIT(ICE_RX_FLEX_DESC_STATUS0_IPV6EXADD_S));
struct ice_rx_ptype_decoded decoded;
bool is_ipv4, is_ipv6;
if (!(status0 & BIT(ICE_RX_FLEX_DESC_STATUS0_L3L4P_S))) {
return;
}
decoded = ice_decode_rx_desc_ptype(ptype);
*flags = 0;
if (!(decoded.known && decoded.outer_ip))
return;
is_ipv4 = (decoded.outer_ip == ICE_RX_PTYPE_OUTER_IP) &&
(decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV4);
is_ipv6 = (decoded.outer_ip == ICE_RX_PTYPE_OUTER_IP) &&
(decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV6);
if (!(status0 & xsum_errors)) {
if (is_ipv4)
*flags |= CSUM_L3_CALC | CSUM_L3_VALID;
switch (decoded.inner_prot) {
case ICE_RX_PTYPE_INNER_PROT_TCP:
case ICE_RX_PTYPE_INNER_PROT_UDP:
case ICE_RX_PTYPE_INNER_PROT_SCTP:
*flags |= CSUM_L4_CALC | CSUM_L4_VALID;
*data |= htons(0xffff);
break;
default:
break;
}
return;
}
if (is_ipv6 && (status0 & BIT(ICE_RX_FLEX_DESC_STATUS0_IPV6EXADD_S))) {
rxq->stats.cso[ICE_CSO_STAT_RX_IP6_ERR]++;
return;
}
if (status0 & l3_error) {
if (is_ipv4) {
rxq->stats.cso[ICE_CSO_STAT_RX_IP4_ERR]++;
*flags |= CSUM_L3_CALC;
} else {
rxq->stats.cso[ICE_CSO_STAT_RX_L3_ERR]++;
}
return;
} else if (is_ipv4) {
*flags |= CSUM_L3_CALC | CSUM_L3_VALID;
}
if (status0 & l4_error) {
switch (decoded.inner_prot) {
case ICE_RX_PTYPE_INNER_PROT_TCP:
rxq->stats.cso[ICE_CSO_STAT_RX_TCP_ERR]++;
*flags |= CSUM_L4_CALC;
break;
case ICE_RX_PTYPE_INNER_PROT_UDP:
rxq->stats.cso[ICE_CSO_STAT_RX_UDP_ERR]++;
*flags |= CSUM_L4_CALC;
break;
case ICE_RX_PTYPE_INNER_PROT_SCTP:
rxq->stats.cso[ICE_CSO_STAT_RX_SCTP_ERR]++;
*flags |= CSUM_L4_CALC;
break;
default:
rxq->stats.cso[ICE_CSO_STAT_RX_L4_ERR]++;
}
}
}
static inline int
ice_ptype_to_hash(u16 ptype)
{
struct ice_rx_ptype_decoded decoded;
if (ptype >= ARRAY_SIZE(ice_ptype_lkup))
return M_HASHTYPE_OPAQUE;
decoded = ice_decode_rx_desc_ptype(ptype);
if (!decoded.known)
return M_HASHTYPE_OPAQUE;
if (decoded.outer_ip == ICE_RX_PTYPE_OUTER_L2)
return M_HASHTYPE_OPAQUE;
if (decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV6) {
switch (decoded.inner_prot) {
case ICE_RX_PTYPE_INNER_PROT_TCP:
return M_HASHTYPE_RSS_TCP_IPV6;
case ICE_RX_PTYPE_INNER_PROT_UDP:
return M_HASHTYPE_RSS_UDP_IPV6;
default:
return M_HASHTYPE_RSS_IPV6;
}
}
if (decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV4) {
switch (decoded.inner_prot) {
case ICE_RX_PTYPE_INNER_PROT_TCP:
return M_HASHTYPE_RSS_TCP_IPV4;
case ICE_RX_PTYPE_INNER_PROT_UDP:
return M_HASHTYPE_RSS_UDP_IPV4;
default:
return M_HASHTYPE_RSS_IPV4;
}
}
return M_HASHTYPE_OPAQUE;
}
#endif