/* Transmit processing * * One queue controller peripheral queue is used for transmit. The * driver en-queues packets for transmit by advancing the write * pointer. The device indicates that packets have transmitted by * advancing the read pointer. The driver maintains a local copy of * the read and write pointer in @struct nfp_net_tx_ring. The driver * keeps @wr_p in sync with the queue controller write pointer and can * determine how many packets have been transmitted by comparing its * copy of the read pointer @rd_p with the read pointer maintained by * the queue controller peripheral.
*/
/* Wrappers for deciding when to stop and restart TX queues */ staticint nfp_nfd3_tx_ring_should_wake(struct nfp_net_tx_ring *tx_ring)
{ return !nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS * 4);
}
/** * nfp_nfd3_tx_ring_stop() - stop tx ring * @nd_q: netdev queue * @tx_ring: driver tx queue structure * * Safely stop TX ring. Remember that while we are running .start_xmit() * someone else may be cleaning the TX ring completions so we need to be * extra careful here.
*/ staticvoid
nfp_nfd3_tx_ring_stop(struct netdev_queue *nd_q, struct nfp_net_tx_ring *tx_ring)
{
netif_tx_stop_queue(nd_q);
/* We can race with the TX completion out of NAPI so recheck */
smp_mb(); if (unlikely(nfp_nfd3_tx_ring_should_wake(tx_ring)))
netif_tx_start_queue(nd_q);
}
/** * nfp_nfd3_tx_tso() - Set up Tx descriptor for LSO * @r_vec: per-ring structure * @txbuf: Pointer to driver soft TX descriptor * @txd: Pointer to HW TX descriptor * @skb: Pointer to SKB * @md_bytes: Prepend length * * Set up Tx descriptor for LSO, do nothing for non-LSO skbs. * Return error on packet header greater than maximum supported LSO header size.
*/ staticvoid
nfp_nfd3_tx_tso(struct nfp_net_r_vector *r_vec, struct nfp_nfd3_tx_buf *txbuf, struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb, u32 md_bytes)
{
u32 l3_offset, l4_offset, hdrlen, l4_hdrlen;
u16 mss;
/** * nfp_nfd3_tx_csum() - Set TX CSUM offload flags in TX descriptor * @dp: NFP Net data path struct * @r_vec: per-ring structure * @txbuf: Pointer to driver soft TX descriptor * @txd: Pointer to TX descriptor * @skb: Pointer to SKB * * This function sets the TX checksum flags in the TX descriptor based * on the configuration and the protocol of the packet to be transmitted.
*/ staticvoid
nfp_nfd3_tx_csum(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, struct nfp_nfd3_tx_buf *txbuf, struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb)
{ struct ipv6hdr *ipv6h; struct iphdr *iph;
u8 l4_hdr;
if (!(dp->ctrl & NFP_NET_CFG_CTRL_TXCSUM)) return;
if (skb->ip_summed != CHECKSUM_PARTIAL) return;
txd->flags |= NFD3_DESC_TX_CSUM; if (skb->encapsulation)
txd->flags |= NFD3_DESC_TX_ENCAP;
if (unlikely(skb_cow_head(skb, md_bytes))) return -ENOMEM;
data = skb_push(skb, md_bytes) + md_bytes; if (md_dst) {
data -= NFP_NET_META_PORTID_SIZE;
put_unaligned_be32(md_dst->u.port_info.port_id, data);
meta_id = NFP_NET_META_PORTID;
} if (tls_handle) { /* conn handle is opaque, we just use u64 to be able to quickly * compare it to zero
*/
data -= NFP_NET_META_CONN_HANDLE_SIZE;
memcpy(data, &tls_handle, sizeof(tls_handle));
meta_id <<= NFP_NET_META_FIELD_SIZE;
meta_id |= NFP_NET_META_CONN_HANDLE;
} if (vlan_insert) {
data -= NFP_NET_META_VLAN_SIZE; /* data type of skb->vlan_proto is __be16 * so it fills metadata without calling put_unaligned_be16
*/
memcpy(data, &skb->vlan_proto, sizeof(skb->vlan_proto));
put_unaligned_be16(skb_vlan_tag_get(skb), data + sizeof(skb->vlan_proto));
meta_id <<= NFP_NET_META_FIELD_SIZE;
meta_id |= NFP_NET_META_VLAN;
} if (*ipsec) {
data -= NFP_NET_META_IPSEC_SIZE;
put_unaligned_be32(offload_info.seq_hi, data);
data -= NFP_NET_META_IPSEC_SIZE;
put_unaligned_be32(offload_info.seq_low, data);
data -= NFP_NET_META_IPSEC_SIZE;
put_unaligned_be32(offload_info.handle - 1, data);
meta_id <<= NFP_NET_META_IPSEC_FIELD_SIZE;
meta_id |= NFP_NET_META_IPSEC << 8 | NFP_NET_META_IPSEC << 4 | NFP_NET_META_IPSEC;
}
data -= sizeof(meta_id);
put_unaligned_be32(meta_id, data);
/* Start with the head skbuf */
dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb),
DMA_TO_DEVICE); if (dma_mapping_error(dp->dev, dma_addr)) goto err_dma_err;
wr_idx = D_IDX(tx_ring, tx_ring->wr_p);
/* Stash the soft descriptor of the head then initialize it */
txbuf = &tx_ring->txbufs[wr_idx];
txbuf->skb = skb;
txbuf->dma_addr = dma_addr;
txbuf->fidx = -1;
txbuf->pkt_cnt = 1;
txbuf->real_len = skb->len;
/* Fill freelist descriptor */
rx_ring->rxds[wr_idx].fld.reserved = 0;
rx_ring->rxds[wr_idx].fld.meta_len_dd = 0; /* DMA address is expanded to 48-bit width in freelist for NFP3800, * so the *_48b macro is used accordingly, it's also OK to fill * a 40-bit address since the top 8 bits are get set to 0.
*/
nfp_desc_set_dma_addr_48b(&rx_ring->rxds[wr_idx].fld,
dma_addr + dp->rx_dma_off);
rx_ring->wr_p++; if (!(rx_ring->wr_p % NFP_NET_FL_BATCH)) { /* Update write pointer of the freelist queue. Make * sure all writes are flushed before telling the hardware.
*/
wmb();
nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, NFP_NET_FL_BATCH);
}
}
/** * nfp_nfd3_rx_ring_fill_freelist() - Give buffers from the ring to FW * @dp: NFP Net data path struct * @rx_ring: RX ring to fill
*/ void nfp_nfd3_rx_ring_fill_freelist(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring)
{ unsignedint i;
if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) return nfp_net_xsk_rx_ring_fill_freelist(rx_ring);
for (i = 0; i < rx_ring->cnt - 1; i++)
nfp_nfd3_rx_give_one(dp, rx_ring, rx_ring->rxbufs[i].frag,
rx_ring->rxbufs[i].dma_addr);
}
/** * nfp_nfd3_rx_csum_has_errors() - group check if rxd has any csum errors * @flags: RX descriptor flags field in CPU byte order
*/ staticint nfp_nfd3_rx_csum_has_errors(u16 flags)
{
u16 csum_all_checked, csum_all_ok;
if (nfp_nfd3_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) {
u64_stats_update_begin(&r_vec->rx_sync);
r_vec->hw_csum_rx_error++;
u64_stats_update_end(&r_vec->rx_sync); return;
}
/* Assume that the firmware will never report inner CSUM_OK unless outer * L4 headers were successfully parsed. FW will always report zero UDP * checksum as CSUM_OK.
*/ if (rxd->rxd.flags & PCIE_DESC_RX_TCP_CSUM_OK ||
rxd->rxd.flags & PCIE_DESC_RX_UDP_CSUM_OK) {
__skb_incr_checksum_unnecessary(skb);
u64_stats_update_begin(&r_vec->rx_sync);
r_vec->hw_csum_rx_ok++;
u64_stats_update_end(&r_vec->rx_sync);
}
while (meta_info) { switch (meta_info & NFP_NET_META_FIELD_MASK) { case NFP_NET_META_HASH:
meta_info >>= NFP_NET_META_FIELD_SIZE;
nfp_nfd3_set_hash(netdev, meta,
meta_info & NFP_NET_META_FIELD_MASK,
(__be32 *)data);
data += 4; break; case NFP_NET_META_MARK:
meta->mark = get_unaligned_be32(data);
data += 4; break; case NFP_NET_META_VLAN:
vlan_info = get_unaligned_be32(data); if (FIELD_GET(NFP_NET_META_VLAN_STRIP, vlan_info)) {
meta->vlan.stripped = true;
meta->vlan.tpid = FIELD_GET(NFP_NET_META_VLAN_TPID_MASK,
vlan_info);
meta->vlan.tci = FIELD_GET(NFP_NET_META_VLAN_TCI_MASK,
vlan_info);
}
data += 4; break; case NFP_NET_META_PORTID:
meta->portid = get_unaligned_be32(data);
data += 4; break; case NFP_NET_META_CSUM:
meta->csum_type = CHECKSUM_COMPLETE;
meta->csum =
(__force __wsum)get_unaligned((u32 *)data);
data += 4; break; case NFP_NET_META_RESYNC_INFO: if (nfp_net_tls_rx_resync_req(netdev, data, pkt,
pkt_len)) returnfalse;
data += sizeof(struct nfp_net_tls_resync_req); break; #ifdef CONFIG_NFP_NET_IPSEC case NFP_NET_META_IPSEC: /* Note: IPsec packet will have zero saidx, so need add 1 * to indicate packet is IPsec packet within driver.
*/
meta->ipsec_saidx = get_unaligned_be32(data) + 1;
data += 4; break; #endif default: returntrue;
}
meta_info >>= NFP_NET_META_FIELD_SIZE;
}
return data != pkt;
}
staticvoid
nfp_nfd3_rx_drop(conststruct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring, struct nfp_net_rx_buf *rxbuf, struct sk_buff *skb)
{
u64_stats_update_begin(&r_vec->rx_sync);
r_vec->rx_drops++; /* If we have both skb and rxbuf the replacement buffer allocation * must have failed, count this as an alloc failure.
*/ if (skb && rxbuf)
r_vec->rx_replace_buf_alloc_fail++;
u64_stats_update_end(&r_vec->rx_sync);
/* skb is build based on the frag, free_skb() would free the frag * so to be able to reuse it we need an extra ref.
*/ if (skb && rxbuf && skb->head == rxbuf->frag)
page_ref_inc(virt_to_head_page(rxbuf->frag)); if (rxbuf)
nfp_nfd3_rx_give_one(dp, rx_ring, rxbuf->frag, rxbuf->dma_addr); if (skb)
dev_kfree_skb_any(skb);
}
/** * nfp_nfd3_rx() - receive up to @budget packets on @rx_ring * @rx_ring: RX ring to receive from * @budget: NAPI budget * * Note, this function is separated out from the napi poll function to * more cleanly separate packet receive code from other bookkeeping * functions performed in the napi poll function. * * Return: Number of packets received.
*/ staticint nfp_nfd3_rx(struct nfp_net_rx_ring *rx_ring, int budget)
{ struct nfp_net_r_vector *r_vec = rx_ring->r_vec; struct nfp_net_dp *dp = &r_vec->nfp_net->dp; struct nfp_net_tx_ring *tx_ring; struct bpf_prog *xdp_prog; int idx, pkts_polled = 0; bool xdp_tx_cmpl = false; unsignedint true_bufsz; struct sk_buff *skb; struct xdp_buff xdp;
rxd = &rx_ring->rxds[idx]; if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) break;
/* Memory barrier to ensure that we won't do other reads * before the DD bit.
*/
dma_rmb();
memset(&meta, 0, sizeof(meta));
rx_ring->rd_p++;
pkts_polled++;
rxbuf = &rx_ring->rxbufs[idx]; /* < meta_len > * <-- [rx_offset] --> * --------------------------------------------------------- * | [XX] | metadata | packet | XXXX | * --------------------------------------------------------- * <---------------- data_len ---------------> * * The rx_offset is fixed for all packets, the meta_len can vary * on a packet by packet basis. If rx_offset is set to zero * (_RX_OFFSET_DYNAMIC) metadata starts at the beginning of the * buffer and is immediately followed by the packet (no [XX]).
*/
meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK;
data_len = le16_to_cpu(rxd->rxd.data_len);
pkt_len = data_len - meta_len;
if (WARN_ON_ONCE(skb_shinfo(skb)->nr_frags)) {
nn_dp_warn(dp, "Driver's CTRL TX does not implement gather\n"); goto err_free;
}
if (unlikely(nfp_net_tx_full(tx_ring, 1))) {
u64_stats_update_begin(&r_vec->tx_sync);
r_vec->tx_busy++;
u64_stats_update_end(&r_vec->tx_sync); if (!old)
__skb_queue_tail(&r_vec->queue, skb); else
__skb_queue_head(&r_vec->queue, skb); returntrue;
}
if (nfp_app_ctrl_has_meta(nn->app)) { if (unlikely(skb_headroom(skb) < 8)) {
nn_dp_warn(dp, "CTRL TX on skb without headroom\n"); goto err_free;
}
meta_len = 8;
put_unaligned_be32(NFP_META_PORT_ID_CTRL, skb_push(skb, 4));
put_unaligned_be32(NFP_NET_META_PORTID, skb_push(skb, 4));
}
/* Start with the head skbuf */
dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb),
DMA_TO_DEVICE); if (dma_mapping_error(dp->dev, dma_addr)) goto err_dma_warn;
wr_idx = D_IDX(tx_ring, tx_ring->wr_p);
/* Stash the soft descriptor of the head then initialize it */
txbuf = &tx_ring->txbufs[wr_idx];
txbuf->skb = skb;
txbuf->dma_addr = dma_addr;
txbuf->fidx = -1;
txbuf->pkt_cnt = 1;
txbuf->real_len = real_len;
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.