/* Pass a received packet up through the generic LRO stack * * Handles driverlink veto, and passes the fragment up via * the appropriate LRO method */ static void efx_rx_packet_lro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf, bool checksummed) { struct napi_struct *napi = &channel->napi_str; gro_result_t gro_result; /* Pass the skb/page into the LRO engine */ if (rx_buf->page) { struct page *page = rx_buf->page; struct sk_buff *skb; EFX_BUG_ON_PARANOID(rx_buf->skb); rx_buf->page = NULL; skb = napi_get_frags(napi); if (!skb) { put_page(page); return; } skb_shinfo(skb)->frags[0].page = page; skb_shinfo(skb)->frags[0].page_offset = efx_rx_buf_offset(rx_buf); skb_shinfo(skb)->frags[0].size = rx_buf->len; skb_shinfo(skb)->nr_frags = 1; skb->len = rx_buf->len; skb->data_len = rx_buf->len; skb->truesize += rx_buf->len; skb->ip_summed = checksummed ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE; skb_record_rx_queue(skb, channel->channel); gro_result = napi_gro_frags(napi); } else { struct sk_buff *skb = rx_buf->skb; EFX_BUG_ON_PARANOID(!skb); EFX_BUG_ON_PARANOID(!checksummed); rx_buf->skb = NULL; gro_result = napi_gro_receive(napi, skb); } if (gro_result == GRO_NORMAL) { channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; } else if (gro_result != GRO_DROP) { channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO; channel->irq_mod_score += 2; } }
/* Pass a received packet up through GRO. GRO can handle pages * regardless of checksum state and skbs with a good checksum. */ static void efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf, unsigned int n_frags, u8 *eh) { struct napi_struct *napi = &channel->napi_str; gro_result_t gro_result; struct efx_nic *efx = channel->efx; struct sk_buff *skb; skb = napi_get_frags(napi); if (unlikely(!skb)) { struct efx_rx_queue *rx_queue; rx_queue = efx_channel_get_rx_queue(channel); efx_free_rx_buffers(rx_queue, rx_buf, n_frags); return; } if (efx->net_dev->features & NETIF_F_RXHASH) skb_set_hash(skb, efx_rx_buf_hash(efx, eh), PKT_HASH_TYPE_L3); skb->ip_summed = ((rx_buf->flags & EFX_RX_PKT_CSUMMED) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE); for (;;) { skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, rx_buf->page, rx_buf->page_offset, rx_buf->len); rx_buf->page = NULL; skb->len += rx_buf->len; if (skb_shinfo(skb)->nr_frags == n_frags) break; rx_buf = efx_rx_buf_next(&channel->rx_queue, rx_buf); } skb->data_len = skb->len; skb->truesize += n_frags * efx->rx_buffer_truesize; skb_record_rx_queue(skb, channel->rx_queue.core_index); skb_mark_napi_id(skb, &channel->napi_str); gro_result = napi_gro_frags(napi); if (gro_result != GRO_DROP) channel->irq_mod_score += 2; }
/* Pass a received packet up through the generic LRO stack * * Handles driverlink veto, and passes the fragment up via * the appropriate LRO method */ static void efx_rx_packet_lro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf) { struct napi_struct *napi = &channel->napi_str; /* Pass the skb/page into the LRO engine */ if (rx_buf->page) { struct sk_buff *skb = napi_get_frags(napi); if (!skb) { put_page(rx_buf->page); goto out; } skb_shinfo(skb)->frags[0].page = rx_buf->page; skb_shinfo(skb)->frags[0].page_offset = efx_rx_buf_offset(rx_buf); skb_shinfo(skb)->frags[0].size = rx_buf->len; skb_shinfo(skb)->nr_frags = 1; skb->len = rx_buf->len; skb->data_len = rx_buf->len; skb->truesize += rx_buf->len; skb->ip_summed = CHECKSUM_UNNECESSARY; napi_gro_frags(napi); out: EFX_BUG_ON_PARANOID(rx_buf->skb); rx_buf->page = NULL; } else { EFX_BUG_ON_PARANOID(!rx_buf->skb); napi_gro_receive(napi, rx_buf->skb); rx_buf->skb = NULL; } }
int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_cqe *cqe; struct mlx4_en_rx_ring *ring = &priv->rx_ring[cq->ring]; struct skb_frag_struct *skb_frags; struct mlx4_en_rx_desc *rx_desc; struct sk_buff *skb; int index; int nr; unsigned int length; int polled = 0; int ip_summed; if (!priv->port_up) return 0; /* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx * descriptor offset can be deduced from the CQE index instead of * reading 'cqe->index' */ index = cq->mcq.cons_index & ring->size_mask; cqe = &cq->buf[index]; /* Process all completed CQEs */ while (XNOR(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK, cq->mcq.cons_index & cq->size)) { skb_frags = ring->rx_info + (index << priv->log_rx_info); rx_desc = ring->buf + (index << ring->log_stride); /* * make sure we read the CQE after we read the ownership bit */ rmb(); /* Drop packet on bad receive or bad checksum */ if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) == MLX4_CQE_OPCODE_ERROR)) { en_err(priv, "CQE completed in error - vendor " "syndrom:%d syndrom:%d\n", ((struct mlx4_err_cqe *) cqe)->vendor_err_syndrome, ((struct mlx4_err_cqe *) cqe)->syndrome); goto next; } if (unlikely(cqe->badfcs_enc & MLX4_CQE_BAD_FCS)) { en_dbg(RX_ERR, priv, "Accepted frame with bad FCS\n"); goto next; } /* * Packet is OK - process it. */ length = be32_to_cpu(cqe->byte_cnt); ring->bytes += length; ring->packets++; if (likely(dev->features & NETIF_F_RXCSUM)) { if ((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) && (cqe->checksum == cpu_to_be16(0xffff))) { priv->port_stats.rx_chksum_good++; /* This packet is eligible for LRO if it is: * - DIX Ethernet (type interpretation) * - TCP/IP (v4) * - without IP options * - not an IP fragment */ if (dev->features & NETIF_F_GRO) { struct sk_buff *gro_skb = napi_get_frags(&cq->napi); if (!gro_skb) goto next; nr = mlx4_en_complete_rx_desc( priv, rx_desc, skb_frags, skb_shinfo(gro_skb)->frags, ring->page_alloc, length); if (!nr) goto next; skb_shinfo(gro_skb)->nr_frags = nr; gro_skb->len = length; gro_skb->data_len = length; gro_skb->truesize += length; gro_skb->ip_summed = CHECKSUM_UNNECESSARY; if (priv->vlgrp && (cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_VLAN_PRESENT_MASK))) vlan_gro_frags(&cq->napi, priv->vlgrp, be16_to_cpu(cqe->sl_vid)); else napi_gro_frags(&cq->napi); goto next; } /* LRO not possible, complete processing here */ ip_summed = CHECKSUM_UNNECESSARY; } else { ip_summed = CHECKSUM_NONE; priv->port_stats.rx_chksum_none++; } } else { ip_summed = CHECKSUM_NONE; priv->port_stats.rx_chksum_none++; } skb = mlx4_en_rx_skb(priv, rx_desc, skb_frags, ring->page_alloc, length); if (!skb) { priv->stats.rx_dropped++; goto next; } if (unlikely(priv->validate_loopback)) { validate_loopback(priv, skb); goto next; } skb->ip_summed = ip_summed; skb->protocol = eth_type_trans(skb, dev); skb_record_rx_queue(skb, cq->ring); /* Push it up the stack */ if (priv->vlgrp && (be32_to_cpu(cqe->vlan_my_qpn) & MLX4_CQE_VLAN_PRESENT_MASK)) { vlan_hwaccel_receive_skb(skb, priv->vlgrp, be16_to_cpu(cqe->sl_vid)); } else netif_receive_skb(skb); next: ++cq->mcq.cons_index; index = (cq->mcq.cons_index) & ring->size_mask; cqe = &cq->buf[index]; if (++polled == budget) { /* We are here because we reached the NAPI budget - * flush only pending LRO sessions */ goto out; } } out: AVG_PERF_COUNTER(priv->pstats.rx_coal_avg, polled); mlx4_cq_set_ci(&cq->mcq); wmb(); /* ensure HW sees CQ consumer before we post new buffers */ ring->cons = cq->mcq.cons_index; ring->prod += polled; /* Polled descriptors were realocated in place */ mlx4_en_update_rx_prod_db(ring); return polled; }
gro_result_t vlan_gro_frags(struct napi_struct *napi, struct vlan_group *grp, unsigned int vlan_tci) { __vlan_hwaccel_put_tag(napi->skb, vlan_tci); return napi_gro_frags(napi); }