static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, int index, u8 owner) { struct mlx4_en_tx_info *tx_info = &ring->tx_info[index]; struct mlx4_en_tx_desc *tx_desc = ring->buf + index * TXBB_SIZE; struct mlx4_wqe_data_seg *data = (void *) tx_desc + tx_info->data_offset; struct sk_buff *skb = tx_info->skb; struct skb_frag_struct *frag; void *end = ring->buf + ring->buf_size; int frags = skb_shinfo(skb)->nr_frags; int i; __be32 *ptr = (__be32 *)tx_desc; __be32 stamp = cpu_to_be32(STAMP_VAL | (!!owner << STAMP_SHIFT)); /* Optimize the common case when there are no wraparounds */ if (likely((void *) tx_desc + tx_info->nr_txbb * TXBB_SIZE <= end)) { if (!tx_info->inl) { if (tx_info->linear) { dma_unmap_single(priv->ddev, (dma_addr_t) be64_to_cpu(data->addr), be32_to_cpu(data->byte_count), PCI_DMA_TODEVICE); ++data; } for (i = 0; i < frags; i++) { frag = &skb_shinfo(skb)->frags[i]; dma_unmap_page(priv->ddev, (dma_addr_t) be64_to_cpu(data[i].addr), skb_frag_size(frag), PCI_DMA_TODEVICE); } } /* Stamp the freed descriptor */ for (i = 0; i < tx_info->nr_txbb * TXBB_SIZE; i += STAMP_STRIDE) { *ptr = stamp; ptr += STAMP_DWORDS; } } else { if (!tx_info->inl) { if ((void *) data >= end) { data = ring->buf + ((void *)data - end); } if (tx_info->linear) { dma_unmap_single(priv->ddev, (dma_addr_t) be64_to_cpu(data->addr), be32_to_cpu(data->byte_count), PCI_DMA_TODEVICE); ++data; } for (i = 0; i < frags; i++) { /* Check for wraparound before unmapping */ if ((void *) data >= end) data = ring->buf; frag = &skb_shinfo(skb)->frags[i]; dma_unmap_page(priv->ddev, (dma_addr_t) be64_to_cpu(data->addr), skb_frag_size(frag), PCI_DMA_TODEVICE); ++data; } } /* Stamp the freed descriptor */ for (i = 0; i < tx_info->nr_txbb * TXBB_SIZE; i += STAMP_STRIDE) { *ptr = stamp; ptr += STAMP_DWORDS; if ((void *) ptr >= end) { ptr = ring->buf; stamp ^= cpu_to_be32(0x80000000); } } } dev_kfree_skb_any(skb); return tx_info->nr_txbb; }
static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct ibmveth_adapter *adapter = netdev_priv(netdev); unsigned int desc_flags; union ibmveth_buf_desc descs[6]; int last, i; int force_bounce = 0; dma_addr_t dma_addr; /* * veth handles a maximum of 6 segments including the header, so * we have to linearize the skb if there are more than this. */ if (skb_shinfo(skb)->nr_frags > 5 && __skb_linearize(skb)) { netdev->stats.tx_dropped++; goto out; } /* veth can't checksum offload UDP */ if (skb->ip_summed == CHECKSUM_PARTIAL && ((skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->protocol != IPPROTO_TCP) || (skb->protocol == htons(ETH_P_IPV6) && ipv6_hdr(skb)->nexthdr != IPPROTO_TCP)) && skb_checksum_help(skb)) { netdev_err(netdev, "tx: failed to checksum packet\n"); netdev->stats.tx_dropped++; goto out; } desc_flags = IBMVETH_BUF_VALID; if (skb->ip_summed == CHECKSUM_PARTIAL) { unsigned char *buf = skb_transport_header(skb) + skb->csum_offset; desc_flags |= (IBMVETH_BUF_NO_CSUM | IBMVETH_BUF_CSUM_GOOD); /* Need to zero out the checksum */ buf[0] = 0; buf[1] = 0; } retry_bounce: memset(descs, 0, sizeof(descs)); /* * If a linear packet is below the rx threshold then * copy it into the static bounce buffer. This avoids the * cost of a TCE insert and remove. */ if (force_bounce || (!skb_is_nonlinear(skb) && (skb->len < tx_copybreak))) { skb_copy_from_linear_data(skb, adapter->bounce_buffer, skb->len); descs[0].fields.flags_len = desc_flags | skb->len; descs[0].fields.address = adapter->bounce_buffer_dma; if (ibmveth_send(adapter, descs)) { adapter->tx_send_failed++; netdev->stats.tx_dropped++; } else { netdev->stats.tx_packets++; netdev->stats.tx_bytes += skb->len; } goto out; } /* Map the header */ dma_addr = dma_map_single(&adapter->vdev->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); if (dma_mapping_error(&adapter->vdev->dev, dma_addr)) goto map_failed; descs[0].fields.flags_len = desc_flags | skb_headlen(skb); descs[0].fields.address = dma_addr; /* Map the frags */ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; dma_addr = skb_frag_dma_map(&adapter->vdev->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); if (dma_mapping_error(&adapter->vdev->dev, dma_addr)) goto map_failed_frags; descs[i+1].fields.flags_len = desc_flags | skb_frag_size(frag); descs[i+1].fields.address = dma_addr; } if (ibmveth_send(adapter, descs)) { adapter->tx_send_failed++; netdev->stats.tx_dropped++; } else { netdev->stats.tx_packets++; netdev->stats.tx_bytes += skb->len; } dma_unmap_single(&adapter->vdev->dev, descs[0].fields.address, descs[0].fields.flags_len & IBMVETH_BUF_LEN_MASK, DMA_TO_DEVICE); for (i = 1; i < skb_shinfo(skb)->nr_frags + 1; i++) dma_unmap_page(&adapter->vdev->dev, descs[i].fields.address, descs[i].fields.flags_len & IBMVETH_BUF_LEN_MASK, DMA_TO_DEVICE); out: dev_kfree_skb(skb); return NETDEV_TX_OK; map_failed_frags: last = i+1; for (i = 0; i < last; i++) dma_unmap_page(&adapter->vdev->dev, descs[i].fields.address, descs[i].fields.flags_len & IBMVETH_BUF_LEN_MASK, DMA_TO_DEVICE); map_failed: if (!firmware_has_feature(FW_FEATURE_CMO)) netdev_err(netdev, "tx: unable to map xmit buffer\n"); adapter->tx_map_failed++; skb_linearize(skb); force_bounce = 1; goto retry_bounce; }