staticvoid enetc_unmap_tx_buff(struct enetc_bdr *tx_ring, struct enetc_tx_swbd *tx_swbd)
{ /* For XDP_TX, pages come from RX, whereas for the other contexts where * we have is_dma_page_set, those come from skb_frag_dma_map. We need * to match the DMA mapping length, so we need to differentiate those.
*/ if (tx_swbd->is_dma_page)
dma_unmap_page(tx_ring->dev, tx_swbd->dma,
tx_swbd->is_xdp_tx ? PAGE_SIZE : tx_swbd->len,
tx_swbd->dir); else
dma_unmap_single(tx_ring->dev, tx_swbd->dma,
tx_swbd->len, tx_swbd->dir);
tx_swbd->dma = 0;
}
/* Let H/W know BD ring has been updated */ staticvoid enetc_update_tx_ring_tail(struct enetc_bdr *tx_ring)
{ /* includes wmb() */
enetc_wr_reg_hot(tx_ring->tpir, tx_ring->next_to_use);
}
/** * enetc_unwind_tx_frame() - Unwind the DMA mappings of a multi-buffer Tx frame * @tx_ring: Pointer to the Tx ring on which the buffer descriptors are located * @count: Number of Tx buffer descriptors which need to be unmapped * @i: Index of the last successfully mapped Tx buffer descriptor
*/ staticvoid enetc_unwind_tx_frame(struct enetc_bdr *tx_ring, int count, int i)
{ while (count--) { struct enetc_tx_swbd *tx_swbd = &tx_ring->tx_swbd[i];
enetc_free_tx_frame(tx_ring, tx_swbd); if (i == 0)
i = tx_ring->bd_count;
i--;
}
}
/* Update originTimestamp field of Sync packet * - 48 bits seconds field * - 32 bits nanseconds field * * In addition, the UDP checksum needs to be updated * by software after updating originTimestamp field, * otherwise the hardware will calculate the wrong * checksum when updating the correction field and * update it to the packet.
*/
data = skb_mac_header(skb);
new_sec_h = htons((sec >> 32) & 0xffff);
new_sec_l = htonl(sec & 0xffffffff);
new_nsec = htonl(nsec); if (udp) { struct udphdr *uh = udp_hdr(skb);
__be32 old_sec_l, old_nsec;
__be16 old_sec_h;
/* Configure single-step register */
val = ENETC_PM0_SINGLE_STEP_EN;
val |= ENETC_SET_SINGLE_STEP_OFFSET(offset1); if (udp)
val |= ENETC_PM0_SINGLE_STEP_CH;
/* first BD needs frm_len and offload flags set */
txbd_tmp.frm_len = cpu_to_le16(hdr_len + data_len);
txbd_tmp.flags = flags;
/* For the TSO header we do not set the dma address since we do not * want it unmapped when we do cleanup. We still set len so that we * count the bytes sent.
*/
tx_swbd->len = hdr_len;
tx_swbd->do_twostep_tstamp = false;
tx_swbd->check_wb = false;
/* Actually write the header in the BD */
*txbd = txbd_tmp;
/* Add extension BD for VLAN */ if (flags & ENETC_TXBD_FLAGS_EX) { /* Get the next BD */
enetc_bdr_idx_inc(tx_ring, i);
txbd = ENETC_TXBD(*tx_ring, *i);
tx_swbd = &tx_ring->tx_swbd[*i];
prefetchw(txbd);
/* Compute the IP checksum. This is necessary since tso_build_hdr() * already incremented the IP ID field.
*/ if (!tso->ipv6) { struct iphdr *iph = (void *)(hdr + mac_hdr_len);
staticint enetc_lso_count_descs(conststruct sk_buff *skb)
{ /* 4 BDs: 1 BD for LSO header + 1 BD for extended BD + 1 BD * for linear area data but not include LSO header, namely * skb_headlen(skb) - lso_hdr_len (it may be 0, but that's * okay, we only need to consider the worst case). And 1 BD * for gap.
*/ return skb_shinfo(skb)->nr_frags + 4;
}
staticint enetc_lso_get_hdr_len(conststruct sk_buff *skb)
{ int hdr_len, tlen;
/* {frm_len_ext, frm_len} indicates the total length of * large transmit data unit. frm_len contains the 16 least * significant bits and frm_len_ext contains the 4 most * significant bits.
*/
frm_len = lso->total_len & 0xffff;
frm_len_ext = (lso->total_len >> 16) & 0xf;
/* Set the flags of the first BD */
flags = ENETC_TXBD_FLAGS_EX | ENETC_TXBD_FLAGS_CSUM_LSO |
ENETC_TXBD_FLAGS_LSO | ENETC_TXBD_FLAGS_L4CS;
/* For the LSO header we do not set the dma address since * we do not want it unmapped when we do cleanup. We still * set len so that we count the bytes sent.
*/
tx_swbd->len = lso->hdr_len;
tx_swbd->do_twostep_tstamp = false;
tx_swbd->check_wb = false;
/* Actually write the header in the BD */
*txbd = txbd_tmp;
/* Get the next BD, and the next BD is extended BD */
enetc_bdr_idx_inc(tx_ring, i);
txbd = ENETC_TXBD(*tx_ring, *i);
tx_swbd = &tx_ring->tx_swbd[*i];
prefetchw(txbd);
enetc_clear_tx_bd(&txbd_tmp); if (skb_vlan_tag_present(skb)) { /* Setup the VLAN fields */
txbd_tmp.ext.vid = cpu_to_le16(skb_vlan_tag_get(skb));
txbd_tmp.ext.tpid = ENETC_TPID_8021Q;
e_flags = ENETC_TXBD_E_FLAGS_VLAN_INS;
}
/* Go to the next BD */
enetc_bdr_idx_inc(tx_ring, &i);
tx_ring->next_to_use = i;
enetc_update_tx_ring_tail(tx_ring);
return count;
dma_err: do {
tx_swbd = &tx_ring->tx_swbd[i];
enetc_free_tx_frame(tx_ring, tx_swbd); if (i == 0)
i = tx_ring->bd_count;
i--;
} while (--count);
return 0;
}
staticint enetc_map_tx_tso_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
{ struct enetc_ndev_priv *priv = netdev_priv(tx_ring->ndev); int hdr_len, total_len, data_len; struct enetc_tx_swbd *tx_swbd; union enetc_tx_bd *txbd; struct tso_t tso;
__wsum csum, csum2; int count = 0, pos; int err, i, bd_data_num;
/* Initialize the TSO handler, and prepare the first payload */
hdr_len = tso_start(skb, &tso);
total_len = skb->len - hdr_len;
i = tx_ring->next_to_use;
while (total_len > 0) { char *hdr;
/* Get the BD */
txbd = ENETC_TXBD(*tx_ring, i);
tx_swbd = &tx_ring->tx_swbd[i];
prefetchw(txbd);
/* Determine the length of this packet */
data_len = min_t(int, skb_shinfo(skb)->gso_size, total_len);
total_len -= data_len;
/* prepare packet headers: MAC + IP + TCP */
hdr = tx_ring->tso_headers + i * TSO_HEADER_SIZE;
tso_build_hdr(skb, hdr, &tso, data_len, total_len == 0);
/* compute the csum over the L4 header */
csum = enetc_tso_hdr_csum(&tso, skb, hdr, hdr_len, &pos);
count += enetc_map_tx_tso_hdr(tx_ring, skb, tx_swbd, txbd,
&i, hdr_len, data_len);
bd_data_num = 0;
while (data_len > 0) { int size;
size = min_t(int, tso.size, data_len);
/* Advance the index in the BDR */
enetc_bdr_idx_inc(tx_ring, &i);
txbd = ENETC_TXBD(*tx_ring, i);
tx_swbd = &tx_ring->tx_swbd[i];
prefetchw(txbd);
/* Compute the checksum over this segment of data and * add it to the csum already computed (over the L4 * header and possible other data segments).
*/
csum2 = csum_partial(tso.data, size, 0);
csum = csum_block_add(csum, csum2, pos);
pos += size;
err = enetc_map_tx_tso_data(tx_ring, skb, tx_swbd, txbd,
tso.data, size,
size == data_len); if (err) { if (i == 0)
i = tx_ring->bd_count;
i--;
/* Queue one-step Sync packet if already locked */ if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) { if (test_and_set_bit_lock(ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS,
&priv->flags)) {
skb_queue_tail(&priv->tx_skbs, skb); return NETDEV_TX_OK;
}
}
tx_ring = priv->tx_ring[skb->queue_mapping];
if (skb_is_gso(skb)) { /* LSO data unit lengths of up to 256KB are supported */ if (priv->active_offloads & ENETC_F_LSO &&
(skb->len - enetc_lso_get_hdr_len(skb)) <=
ENETC_LSO_MAX_DATA_LEN) { if (enetc_bd_unused(tx_ring) < enetc_lso_count_descs(skb)) {
netif_stop_subqueue(ndev, tx_ring->index); return NETDEV_TX_BUSY;
}
if (likely(enetc_swbd_unused(rx_ring))) {
enetc_reuse_page(rx_ring, &rx_swbd);
/* sync for use by the device */
dma_sync_single_range_for_device(rx_ring->dev, rx_swbd.dma,
rx_swbd.page_offset,
ENETC_RXB_DMA_SIZE_XDP,
rx_swbd.dir);
rx_ring->stats.recycles++;
} else { /* RX ring is already full, we need to unmap and free the * page, since there's nothing useful we can do with it.
*/
rx_ring->stats.recycle_failures++;
if (tx_swbd->qbv_en &&
txbd->wb.status & ENETC_TXBD_STATS_WIN)
tx_win_drop++;
}
if (tx_swbd->is_xdp_tx)
enetc_recycle_xdp_tx_buff(tx_ring, tx_swbd); elseif (likely(tx_swbd->dma))
enetc_unmap_tx_buff(tx_ring, tx_swbd);
if (xdp_frame) {
xdp_return_frame(xdp_frame);
} elseif (skb) { if (unlikely(skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)) { /* Start work to release lock for next one-step * timestamping packet. And send one skb in * tx_skbs queue if has.
*/
schedule_work(&priv->tx_onestep_tstamp);
} elseif (unlikely(do_twostep_tstamp)) {
enetc_tstamp_tx(skb, tstamp);
do_twostep_tstamp = false;
}
napi_consume_skb(skb, napi_budget);
}
tx_byte_cnt += tx_swbd->len; /* Scrub the swbd here so we don't have to do that * when we reuse it during xmit
*/
memset(tx_swbd, 0, sizeof(*tx_swbd));
bds_to_clean--;
tx_swbd++;
i++; if (unlikely(i == tx_ring->bd_count)) {
i = 0;
tx_swbd = tx_ring->tx_swbd;
}
if (le16_to_cpu(rxbd->r.flags) & ENETC_RXBD_FLAG_TSTMP) {
lo = enetc_rd_reg_hot(hw->reg + ENETC_SICTR0);
hi = enetc_rd_reg_hot(hw->reg + ENETC_SICTR1);
rxbd = enetc_rxbd_ext(rxbd);
tstamp_lo = le32_to_cpu(rxbd->ext.tstamp); if (lo <= tstamp_lo)
hi -= 1;
if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) &&
(priv->active_offloads & ENETC_F_RX_TSTAMP))
enetc_get_rx_tstamp(rx_ring->ndev, rxbd, skb);
}
/* This gets called during the non-XDP NAPI poll cycle as well as on XDP_PASS, * so it needs to work with both DMA_FROM_DEVICE as well as DMA_BIDIRECTIONAL * mapped buffers.
*/ staticstruct enetc_rx_swbd *enetc_get_rx_buff(struct enetc_bdr *rx_ring, int i, u16 size)
{ struct enetc_rx_swbd *rx_swbd = &rx_ring->rx_swbd[i];
/* When set, the outer VLAN header is extracted and reported * in the receive buffer descriptor. So rx_byte_cnt should * add the length of the extracted VLAN header.
*/ if (bd_status & ENETC_RXBD_FLAG_VLAN)
rx_byte_cnt += VLAN_HLEN;
rx_byte_cnt += skb->len + ETH_HLEN;
rx_frm_cnt++;
staticvoid enetc_xdp_map_tx_buff(struct enetc_bdr *tx_ring, int i, struct enetc_tx_swbd *tx_swbd, int frm_len)
{ union enetc_tx_bd *txbd = ENETC_TXBD(*tx_ring, i);
/* Puts in the TX ring one XDP frame, mapped as an array of TX software buffer * descriptors.
*/ staticbool enetc_xdp_tx(struct enetc_bdr *tx_ring, struct enetc_tx_swbd *xdp_tx_arr, int num_tx_swbd)
{ struct enetc_tx_swbd *tmp_tx_swbd = xdp_tx_arr; int i, k, frm_len = tmp_tx_swbd->len;
if (unlikely(enetc_bd_unused(tx_ring) < ENETC_TXBDS_NEEDED(num_tx_swbd))) returnfalse;
while (unlikely(!tmp_tx_swbd->is_eof)) {
tmp_tx_swbd++;
frm_len += tmp_tx_swbd->len;
}
i = tx_ring->next_to_use;
for (k = 0; k < num_tx_swbd; k++) { struct enetc_tx_swbd *xdp_tx_swbd = &xdp_tx_arr[k];
enetc_xdp_map_tx_buff(tx_ring, i, xdp_tx_swbd, frm_len);
/* last BD needs 'F' bit set */ if (xdp_tx_swbd->is_eof) { union enetc_tx_bd *txbd = ENETC_TXBD(*tx_ring, i);
txbd->flags = ENETC_TXBD_FLAGS_F;
}
enetc_bdr_idx_inc(tx_ring, &i);
}
tx_ring->next_to_use = i;
returntrue;
}
staticint enetc_xdp_frame_to_xdp_tx_swbd(struct enetc_bdr *tx_ring, struct enetc_tx_swbd *xdp_tx_arr, struct xdp_frame *xdp_frame)
{ struct enetc_tx_swbd *xdp_tx_swbd = &xdp_tx_arr[0]; struct skb_shared_info *shinfo; void *data = xdp_frame->data; int len = xdp_frame->len;
skb_frag_t *frag;
dma_addr_t dma; unsignedint f; int n = 0;
for (f = 0, frag = &shinfo->frags[0]; f < shinfo->nr_frags;
f++, frag++) {
data = skb_frag_address(frag);
len = skb_frag_size(frag);
dma = dma_map_single(tx_ring->dev, data, len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(tx_ring->dev, dma))) { /* Undo the DMA mapping for all fragments */ while (--n >= 0)
enetc_unmap_tx_buff(tx_ring, &xdp_tx_arr[n]);
for (k = 0; k < num_frames; k++) {
xdp_tx_bd_cnt = enetc_xdp_frame_to_xdp_tx_swbd(tx_ring,
xdp_redirect_arr,
frames[k]); if (unlikely(xdp_tx_bd_cnt < 0)) break;
if (unlikely(!enetc_xdp_tx(tx_ring, xdp_redirect_arr,
xdp_tx_bd_cnt))) { for (i = 0; i < xdp_tx_bd_cnt; i++)
enetc_unmap_tx_buff(tx_ring,
&xdp_redirect_arr[i]);
tx_ring->stats.xdp_tx_drops++; break;
}
xdp_tx_frm_cnt++;
}
if (unlikely((flags & XDP_XMIT_FLUSH) || k != xdp_tx_frm_cnt))
enetc_update_tx_ring_tail(tx_ring);
/* Convert RX buffer descriptors to TX buffer descriptors. These will be * recycled back into the RX ring in enetc_clean_tx_ring.
*/ staticint enetc_rx_swbd_to_xdp_tx_swbd(struct enetc_tx_swbd *xdp_tx_arr, struct enetc_bdr *rx_ring, int rx_ring_first, int rx_ring_last)
{ int n = 0;
/* When set, the outer VLAN header is extracted and reported * in the receive buffer descriptor. So rx_byte_cnt should * add the length of the extracted VLAN header.
*/ if (bd_status & ENETC_RXBD_FLAG_VLAN)
rx_byte_cnt += VLAN_HLEN;
rx_byte_cnt += xdp_get_buff_len(&xdp_buff);
xdp_act = bpf_prog_run_xdp(prog, &xdp_buff);
switch (xdp_act) { default:
bpf_warn_invalid_xdp_action(rx_ring->ndev, prog, xdp_act);
fallthrough; case XDP_ABORTED:
trace_xdp_exception(rx_ring->ndev, prog, xdp_act);
fallthrough; case XDP_DROP:
enetc_xdp_drop(rx_ring, orig_i, i);
rx_ring->stats.xdp_drops++; break; case XDP_PASS:
skb = xdp_build_skb_from_buff(&xdp_buff); /* Probably under memory pressure, stop NAPI */ if (unlikely(!skb)) {
enetc_xdp_drop(rx_ring, orig_i, i);
rx_ring->stats.xdp_drops++; goto out;
}
enetc_get_offloads(rx_ring, orig_rxbd, skb);
/* These buffers are about to be owned by the stack. * Update our buffer cache (the rx_swbd array elements) * with their other page halves.
*/
enetc_bulk_flip_buff(rx_ring, orig_i, i);
enetc_unlock_mdio();
napi_gro_receive(napi, skb);
enetc_lock_mdio(); break; case XDP_TX:
tx_ring = priv->xdp_tx_ring[rx_ring->index]; if (unlikely(test_bit(ENETC_TX_DOWN, &priv->flags))) {
enetc_xdp_drop(rx_ring, orig_i, i);
tx_ring->stats.xdp_tx_drops++; break;
}
if (!enetc_xdp_tx(tx_ring, xdp_tx_arr, xdp_tx_bd_cnt)) {
enetc_xdp_drop(rx_ring, orig_i, i);
tx_ring->stats.xdp_tx_drops++;
} else {
tx_ring->stats.xdp_tx++;
rx_ring->xdp.xdp_tx_in_flight += xdp_tx_bd_cnt;
xdp_tx_frm_cnt++; /* The XDP_TX enqueue was successful, so we * need to scrub the RX software BDs because * the ownership of the buffers no longer * belongs to the RX ring, and we must prevent * enetc_refill_rx_ring() from reusing * rx_swbd->page.
*/ while (orig_i != i) {
rx_ring->rx_swbd[orig_i].page = NULL;
enetc_bdr_idx_inc(rx_ring, &orig_i);
}
} break; case XDP_REDIRECT:
enetc_unlock_mdio();
err = xdp_do_redirect(rx_ring->ndev, &xdp_buff, prog);
enetc_lock_mdio(); if (unlikely(err)) {
enetc_xdp_drop(rx_ring, orig_i, i);
rx_ring->stats.xdp_redirect_failures++;
} else {
enetc_bulk_flip_buff(rx_ring, orig_i, i);
xdp_redirect_frm_cnt++;
rx_ring->stats.xdp_redirect++;
}
}
/* find out how many of various resources we have to work with */
val = enetc_rd(hw, ENETC_SICAPR0);
si->num_rx_rings = (val >> 16) & 0xff;
si->num_tx_rings = val & 0xff;
val = enetc_rd(hw, ENETC_SIPCAPR0); if (val & ENETC_SIPCAPR0_RFS) {
val = enetc_rd(hw, ENETC_SIRFSCAPR);
si->num_fs_entries = ENETC_SIRFSCAPR_GET_NUM_RFS(val);
si->num_fs_entries = min(si->num_fs_entries, ENETC_MAX_RFS_SIZE);
} else { /* ENETC which not supports RFS */
si->num_fs_entries = 0;
}
si->num_rss = 0;
val = enetc_rd(hw, ENETC_SIPCAPR0); if (val & ENETC_SIPCAPR0_RSS) {
u32 rss;
/* Enable all available TX rings in order to configure as many * priorities as possible, when needed. * TODO: Make # of TX rings run-time configurable
*/
priv->num_rx_rings = min_t(int, cpus, si->num_rx_rings);
priv->num_tx_rings = si->num_tx_rings;
priv->bdr_int_num = priv->num_rx_rings;
priv->ic_mode = ENETC_IC_RX_ADAPTIVE | ENETC_IC_TX_MANUAL;
priv->tx_ictt = enetc_usecs_to_cycles(600, priv->sysclk_freq);
}
EXPORT_SYMBOL_GPL(enetc_init_si_rings_params);
int enetc_alloc_si_resources(struct enetc_ndev_priv *priv)
{ struct enetc_si *si = priv->si;
priv->cls_rules = kcalloc(si->num_fs_entries, sizeof(*priv->cls_rules),
GFP_KERNEL); if (!priv->cls_rules) return -ENOMEM;
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.