/* * Copyright (c) 2007 Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *
*/
int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, int cq, int user_prio)
{ struct mlx4_en_dev *mdev = priv->mdev; int err;
/* Optimize the common case when there are no wraparounds */ if (likely((void *)tx_desc +
(tx_info->nr_txbb << LOG_TXBB_SIZE) <= end)) { /* Stamp the freed descriptor */ for (i = 0; i < tx_info->nr_txbb << LOG_TXBB_SIZE;
i += STAMP_STRIDE) {
*ptr = stamp;
ptr += STAMP_DWORDS;
}
} else { /* Stamp the freed descriptor */ for (i = 0; i < tx_info->nr_txbb << LOG_TXBB_SIZE;
i += STAMP_STRIDE) {
*ptr = stamp;
ptr += STAMP_DWORDS; if ((void *)ptr >= end) {
ptr = ring->buf;
stamp ^= cpu_to_be32(0x80000000);
}
}
}
}
INDIRECT_CALLABLE_DECLARE(u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, int index, u64 timestamp, int napi_mode));
if (!tx_info->inl) { if (tx_info->linear)
dma_unmap_single(priv->ddev,
tx_info->map0_dma,
tx_info->map0_byte_count,
DMA_TO_DEVICE); else
dma_unmap_page(priv->ddev,
tx_info->map0_dma,
tx_info->map0_byte_count,
DMA_TO_DEVICE); /* Optimize the common case when there are no wraparounds */ if (likely((void *)tx_desc +
(tx_info->nr_txbb << LOG_TXBB_SIZE) <= end)) { for (i = 1; i < nr_maps; i++) {
data++;
dma_unmap_page(priv->ddev,
(dma_addr_t)be64_to_cpu(data->addr),
be32_to_cpu(data->byte_count),
DMA_TO_DEVICE);
}
} else { if ((void *)data >= end)
data = ring->buf + ((void *)data - end);
for (i = 1; i < nr_maps; i++) {
data++; /* Check for wraparound before unmapping */ if ((void *) data >= end)
data = ring->buf;
dma_unmap_page(priv->ddev,
(dma_addr_t)be64_to_cpu(data->addr),
be32_to_cpu(data->byte_count),
DMA_TO_DEVICE);
}
}
}
napi_consume_skb(skb, napi_mode);
return tx_info->nr_txbb;
}
INDIRECT_CALLABLE_DECLARE(u32 mlx4_en_recycle_tx_desc(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, int index, u64 timestamp, int napi_mode));
/* * To prevent CQ overflow we first update CQ consumer and only then * the ring consumer.
*/
mcq->cons_index = cons_index;
mlx4_cq_set_ci(mcq);
wmb();
/* we want to dirty this cache line once */
WRITE_ONCE(ring->last_nr_txbb, last_nr_txbb);
WRITE_ONCE(ring->cons, ring_cons + txbbs_skipped);
/* Wakeup Tx queue if this stopped, and ring is not full.
*/ if (netif_tx_queue_stopped(ring->tx_queue) &&
!mlx4_en_is_tx_ring_full(ring)) {
netif_tx_wake_queue(ring->tx_queue);
ring->wake_queue++;
}
/* Decide if skb can be inlined in tx descriptor to avoid dma mapping * * It seems strange we do not simply use skb_copy_bits(). * This would allow to inline all skbs iff skb->len <= inline_thold * * Note that caller already checked skb was not a gso packet
*/ staticbool is_inline(int inline_thold, conststruct sk_buff *skb, conststruct skb_shared_info *shinfo, void **pfrag)
{ void *ptr;
if (skb->len > inline_thold || !inline_thold) returnfalse;
if (shinfo->nr_frags == 1) {
ptr = skb_frag_address_safe(&shinfo->frags[0]); if (unlikely(!ptr)) returnfalse;
*pfrag = ptr; returntrue;
} if (shinfo->nr_frags) returnfalse; returntrue;
}
staticint get_real_size(conststruct sk_buff *skb, conststruct skb_shared_info *shinfo, struct net_device *dev, int *lso_header_size, bool *inline_ok, void **pfrag, int *hopbyhop)
{ struct mlx4_en_priv *priv = netdev_priv(dev); int real_size;
if (shinfo->gso_size) {
*inline_ok = false;
*hopbyhop = 0; if (skb->encapsulation) {
*lso_header_size = skb_inner_tcp_all_headers(skb);
} else { /* Detects large IPV6 TCP packets and prepares for removal of * HBH header that has been pushed by ip6_xmit(), * mainly so that tcpdump can dissect them.
*/ if (ipv6_has_hopopt_jumbo(skb))
*hopbyhop = sizeof(struct hop_jumbo_hdr);
*lso_header_size = skb_tcp_all_headers(skb);
}
real_size = CTRL_SIZE + shinfo->nr_frags * DS_SIZE +
ALIGN(*lso_header_size - *hopbyhop + 4, DS_SIZE); if (unlikely(*lso_header_size != skb_headlen(skb))) { /* We add a segment for the skb linear buffer only if
* it contains data */ if (*lso_header_size < skb_headlen(skb))
real_size += DS_SIZE; else { if (netif_msg_tx_err(priv))
en_warn(priv, "Non-linear headers\n"); return 0;
}
}
} else {
*lso_header_size = 0;
*inline_ok = is_inline(priv->prof->inline_thold, skb,
shinfo, pfrag);
void mlx4_en_xmit_doorbell(struct mlx4_en_tx_ring *ring)
{
wmb(); /* Since there is no iowrite*_native() that writes the * value as is, without byteswapping - using the one * the doesn't do byteswapping in the relevant arch * endianness.
*/ #ifdefined(__LITTLE_ENDIAN)
iowrite32( #else
iowrite32be( #endif
(__force u32)ring->doorbell_qpn, ring->doorbell_address);
}
staticvoid mlx4_en_tx_write_desc(struct mlx4_en_tx_ring *ring, struct mlx4_en_tx_desc *tx_desc, union mlx4_wqe_qpn_vlan qpn_vlan, int desc_size, int bf_index,
__be32 op_own, bool bf_ok, bool send_doorbell)
{
tx_desc->ctrl.qpn_vlan = qpn_vlan;
if (bf_ok) {
op_own |= htonl((bf_index & 0xffff) << 8); /* Ensure new descriptor hits memory * before setting ownership of this descriptor to HW
*/
dma_wmb();
tx_desc->ctrl.owner_opcode = op_own;
/* Packet is good - grab an index and transmit it */
index = ring->prod & ring->size_mask;
bf_index = ring->prod;
/* See if we have enough space for whole descriptor TXBB for setting
* SW ownership on next descriptor; if not, use a bounce buffer. */ if (likely(index + nr_txbb <= ring->size))
tx_desc = ring->buf + (index << LOG_TXBB_SIZE); else { if (unlikely(nr_txbb > MLX4_MAX_DESC_TXBBS)) { if (netif_msg_tx_err(priv))
en_warn(priv, "Oversized header or SG list\n"); goto tx_drop_count;
}
tx_desc = (struct mlx4_en_tx_desc *) ring->bounce_buf;
bounce = true;
bf_ok = false;
}
/* Save skb in tx_info ring */
tx_info = &ring->tx_info[index];
tx_info->skb = skb;
tx_info->nr_txbb = nr_txbb;
if (!lso_header_size) {
data = &tx_desc->data;
data_offset = offsetof(struct mlx4_en_tx_desc, data);
} else { int lso_align = ALIGN(lso_header_size - hopbyhop + 4, DS_SIZE);
tx_info->nr_maps = shinfo->nr_frags + tx_info->linear;
data += tx_info->nr_maps - 1;
if (!tx_info->inl) if (!mlx4_en_build_dma_wqe(priv, shinfo, data, skb,
lso_header_size, ring->mr_key,
tx_info)) goto tx_drop_count;
/* * For timestamping add flag to skb_shinfo and * set flag for further reference
*/
tx_info->ts_requested = 0; if (unlikely(ring->hwtstamp_tx_type == HWTSTAMP_TX_ON &&
shinfo->tx_flags & SKBTX_HW_TSTAMP)) {
shinfo->tx_flags |= SKBTX_IN_PROGRESS;
tx_info->ts_requested = 1;
}
/* Prepare ctrl segment apart opcode+ownership, which depends on
* whether LSO is used */
tx_desc->ctrl.srcrb_flags = priv->ctrl_flags; if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { if (!skb->encapsulation)
tx_desc->ctrl.srcrb_flags |= cpu_to_be32(MLX4_WQE_CTRL_IP_CSUM |
MLX4_WQE_CTRL_TCP_UDP_CSUM); else
tx_desc->ctrl.srcrb_flags |= cpu_to_be32(MLX4_WQE_CTRL_IP_CSUM);
ring->tx_csum++;
}
if (priv->flags & MLX4_EN_FLAG_ENABLE_HW_LOOPBACK) { struct ethhdr *ethh;
/* Copy dst mac address to wqe. This allows loopback in eSwitch, * so that VFs and PF can communicate with each other
*/
ethh = (struct ethhdr *)skb->data;
tx_desc->ctrl.srcrb_flags16[0] = get_unaligned((__be16 *)ethh->h_dest);
tx_desc->ctrl.imm = get_unaligned((__be32 *)(ethh->h_dest + 2));
}
/* Handle LSO (TSO) packets */ if (lso_header_size) { int i;
if (proto == IPPROTO_TCP || proto == IPPROTO_UDP)
op_own |= cpu_to_be32(MLX4_WQE_CTRL_IIP | MLX4_WQE_CTRL_ILP); else
op_own |= cpu_to_be32(MLX4_WQE_CTRL_IIP);
}
WRITE_ONCE(ring->prod, ring->prod + nr_txbb);
/* If we used a bounce buffer then copy descriptor back into place */ if (unlikely(bounce))
tx_desc = mlx4_en_bounce_to_desc(priv, ring, index, desc_size);
skb_tx_timestamp(skb);
/* Check available TXBBs And 2K spare for prefetch */
stop_queue = mlx4_en_is_tx_ring_full(ring); if (unlikely(stop_queue)) {
netif_tx_stop_queue(ring->tx_queue);
ring->queue_stopped++;
}
if (unlikely(stop_queue)) { /* If queue was emptied after the if (stop_queue) , and before * the netif_tx_stop_queue() - need to wake the queue, * or else it will remain stopped forever. * Need a memory barrier to make sure ring->cons was not * updated before queue was stopped.
*/
smp_rmb();
if (unlikely(!mlx4_en_is_tx_ring_full(ring))) {
netif_tx_wake_queue(ring->tx_queue);
ring->wake_queue++;
}
} return NETDEV_TX_OK;
/* Ensure new descriptor hits memory * before setting ownership of this descriptor to HW
*/
dma_wmb();
tx_desc->ctrl.owner_opcode = op_own;
ring->xmit_more++;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.