/* gvnic can only transmit from a Registered Segment. * We copy skb payloads into the registered segment before writing Tx * descriptors and ringing the Tx doorbell. * * gve_tx_fifo_* manages the Registered Segment as a FIFO - clients must * free allocations in the order they were allocated.
*/
/* gve_tx_alloc_fifo - Allocate fragment(s) from Tx FIFO * @fifo: FIFO to allocate from * @bytes: Allocation size * @iov: Scatter-gather elements to fill with allocation fragment base/len * * Returns number of valid elements in iov[] or negative on error. * * Allocations from a given FIFO must be externally synchronized but concurrent * allocation and frees are allowed.
*/ staticint gve_tx_alloc_fifo(struct gve_tx_fifo *fifo, size_t bytes, struct gve_tx_iovec iov[2])
{
size_t overflow, padding;
u32 aligned_head; int nfrags = 0;
if (!bytes) return 0;
/* This check happens before we know how much padding is needed to * align to a cacheline boundary for the payload, but that is fine, * because the FIFO head always start aligned, and the FIFO's boundaries * are aligned, so if there is space for the data, there is space for * the padding to the next alignment.
*/
WARN(!gve_tx_fifo_can_alloc(fifo, bytes), "Reached %s when there's not enough space in the fifo", __func__);
if (fifo->head > fifo->size) { /* If the allocation did not fit in the tail fragment of the * FIFO, also use the head fragment.
*/
nfrags++;
overflow = fifo->head - fifo->size;
iov[0].iov_len -= overflow;
iov[1].iov_offset = 0; /* Start of fifo*/
iov[1].iov_len = overflow;
int gve_tx_alloc_rings_gqi(struct gve_priv *priv, struct gve_tx_alloc_rings_cfg *cfg)
{ struct gve_tx_ring *tx = cfg->tx; int total_queues; int err = 0; int i, j;
total_queues = cfg->qcfg->num_queues + cfg->num_xdp_rings; if (total_queues > cfg->qcfg->max_queues) {
netif_err(priv, drv, priv->dev, "Cannot alloc more than the max num of Tx rings\n"); return -EINVAL;
}
tx = kvcalloc(cfg->qcfg->max_queues, sizeof(struct gve_tx_ring),
GFP_KERNEL); if (!tx) return -ENOMEM;
for (i = 0; i < total_queues; i++) {
err = gve_tx_alloc_ring_gqi(priv, cfg, &tx[i], i); if (err) {
netif_err(priv, drv, priv->dev, "Failed to alloc tx ring=%d: err=%d\n",
i, err); goto cleanup;
}
}
for (i = 0; i < cfg->qcfg->num_queues + cfg->qcfg->num_xdp_queues; i++)
gve_tx_free_ring_gqi(priv, &tx[i], cfg);
kvfree(tx);
cfg->tx = NULL;
}
/* gve_tx_avail - Calculates the number of slots available in the ring * @tx: tx ring to check * * Returns the number of slots available * * The capacity of the queue is mask + 1. We don't need to reserve an entry.
**/ staticinline u32 gve_tx_avail(struct gve_tx_ring *tx)
{ return tx->mask + 1 - (tx->req - tx->done);
}
staticinlineint gve_skb_fifo_bytes_required(struct gve_tx_ring *tx, struct sk_buff *skb)
{ int pad_bytes, align_hdr_pad; int bytes; int hlen;
pad_bytes = gve_tx_fifo_pad_alloc_one_frag(&tx->tx_fifo,
hlen); /* We need to take into account the header alignment padding. */
align_hdr_pad = L1_CACHE_ALIGN(hlen) - hlen;
bytes = align_hdr_pad + pad_bytes + skb->len;
return bytes;
}
/* The most descriptors we could need is MAX_SKB_FRAGS + 4 : * 1 for each skb frag * 1 for the skb linear portion * 1 for when tcp hdr needs to be in separate descriptor * 1 if the payload wraps to the beginning of the FIFO * 1 for metadata descriptor
*/ #define MAX_TX_DESC_NEEDED (MAX_SKB_FRAGS + 4) staticvoid gve_tx_unmap_buf(struct device *dev, struct gve_tx_buffer_state *info)
{ if (info->skb) {
dma_unmap_single(dev, dma_unmap_addr(info, dma),
dma_unmap_len(info, len),
DMA_TO_DEVICE);
dma_unmap_len_set(info, len, 0);
} else {
dma_unmap_page(dev, dma_unmap_addr(info, dma),
dma_unmap_len(info, len),
DMA_TO_DEVICE);
dma_unmap_len_set(info, len, 0);
}
}
/* Check if sufficient resources (descriptor ring space, FIFO space) are * available to transmit the given number of bytes.
*/ staticinlinebool gve_can_tx(struct gve_tx_ring *tx, int bytes_required)
{ bool can_alloc = true;
if (!tx->raw_addressing)
can_alloc = gve_tx_fifo_can_alloc(&tx->tx_fifo, bytes_required);
/* Only try to clean if there is hope for TX */ if (to_do + gve_tx_avail(tx) >= MAX_TX_DESC_NEEDED) { if (to_do > 0) {
to_do = min_t(u32, to_do, NAPI_POLL_WEIGHT);
gve_clean_tx_done(priv, tx, to_do, false);
} if (likely(gve_can_tx(tx, bytes_required)))
ret = 0;
} if (ret) { /* No space, so stop the queue */
tx->stop_queue++;
netif_tx_stop_queue(tx->netdev_txq);
}
spin_unlock(&tx->clean_lock);
staticint gve_tx_add_skb_copy(struct gve_priv *priv, struct gve_tx_ring *tx, struct sk_buff *skb)
{ int pad_bytes, hlen, hdr_nfrags, payload_nfrags, l4_hdr_offset; union gve_tx_desc *pkt_desc, *seg_desc; struct gve_tx_buffer_state *info; int mtd_desc_nr = !!skb->l4_hash; bool is_gso = skb_is_gso(skb);
u32 idx = tx->req & tx->mask; int payload_iov = 2; int copy_offset;
u32 next_idx; int i;
info = &tx->info[idx];
pkt_desc = &tx->desc[idx];
l4_hdr_offset = skb_checksum_start_offset(skb); /* If the skb is gso, then we want the tcp header alone in the first segment * otherwise we want the minimum required by the gVNIC spec.
*/
hlen = is_gso ? l4_hdr_offset + tcp_hdrlen(skb) :
min_t(int, GVE_GQ_TX_MIN_PKT_DESC_BYTES, skb->len);
info->skb = skb; /* We don't want to split the header, so if necessary, pad to the end * of the fifo and then put the header at the beginning of the fifo.
*/
pad_bytes = gve_tx_fifo_pad_alloc_one_frag(&tx->tx_fifo, hlen);
hdr_nfrags = gve_tx_alloc_fifo(&tx->tx_fifo, hlen + pad_bytes,
&info->iov[0]);
WARN(!hdr_nfrags, "hdr_nfrags should never be 0!");
payload_nfrags = gve_tx_alloc_fifo(&tx->tx_fifo, skb->len - hlen,
&info->iov[payload_iov]);
l4_hdr_offset = skb_checksum_start_offset(skb); /* If the skb is gso, then we want only up to the tcp header in the first segment * to efficiently replicate on each segment otherwise we want the linear portion * of the skb (which will contain the checksum because skb->csum_start and * skb->csum_offset are given relative to skb->head) in the first segment.
*/
hlen = is_gso ? l4_hdr_offset + tcp_hdrlen(skb) : skb_headlen(skb);
len = skb_headlen(skb);
if (hlen < len) { /* For gso the rest of the linear portion of the skb needs to * be in its own descriptor.
*/
len -= hlen;
addr += hlen;
idx = (idx + 1) & tx->mask;
seg_desc = &tx->desc[idx];
gve_tx_fill_seg_desc(seg_desc, skb_network_offset(skb),
skb_shinfo(skb)->gso_size,
skb_is_gso_v6(skb), is_gso, len, addr);
}
for (i = 0; i < shinfo->nr_frags; i++) { const skb_frag_t *frag = &shinfo->frags[i];
WARN(skb_get_queue_mapping(skb) >= priv->tx_cfg.num_queues, "skb queue index out of range");
tx = &priv->tx[skb_get_queue_mapping(skb)]; if (unlikely(gve_maybe_stop_tx(priv, tx, skb))) { /* We need to ring the txq doorbell -- we have stopped the Tx * queue for want of resources, but prior calls to gve_tx() * may have added descriptors without ringing the doorbell.
*/
/* If the packet is getting sent, we need to update the skb */ if (nsegs) {
netdev_tx_sent_queue(tx->netdev_txq, skb->len);
skb_tx_timestamp(skb);
tx->req += nsegs;
} else {
dev_kfree_skb_any(skb);
}
if (!netif_xmit_stopped(tx->netdev_txq) && netdev_xmit_more()) return NETDEV_TX_OK;
/* Give packets to NIC. Even if this packet failed to send the doorbell * might need to be rung because of xmit_more.
*/
gve_tx_put_doorbell(priv, tx->q_resources, tx->req); return NETDEV_TX_OK;
}
/* start the queue if we've stopped it */ #ifndef CONFIG_BQL /* Make sure that the doorbells are synced */
smp_mb(); #endif if (try_to_wake && netif_tx_queue_stopped(tx->netdev_txq) &&
likely(gve_can_tx(tx, GVE_TX_START_THRESH))) {
tx->wake_queue++;
netif_tx_wake_queue(tx->netdev_txq);
}
/* Find out how much work there is to be done */
nic_done = gve_tx_load_event_counter(priv, tx);
to_do = min_t(u32, (nic_done - tx->done), budget);
gve_clean_xdp_done(priv, tx, to_do);
/* If we still have work we want to repoll */ return nic_done != tx->done;
}
/* If budget is 0, do all the work */ if (budget == 0)
budget = INT_MAX;
/* In TX path, it may try to clean completed pkts in order to xmit, * to avoid cleaning conflict, use spin_lock(), it yields better * concurrency between xmit/clean than netif's lock.
*/
spin_lock(&tx->clean_lock); /* Find out how much work there is to be done */
nic_done = gve_tx_load_event_counter(priv, tx);
to_do = min_t(u32, (nic_done - tx->done), budget);
gve_clean_tx_done(priv, tx, to_do, true);
spin_unlock(&tx->clean_lock); /* If we still have work we want to repoll */ return nic_done != tx->done;
}
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.