status = otx2_atomic64_fetch_add(incr, pfvf->cq_op_addr);
if (unlikely(status & BIT_ULL(CQ_OP_STAT_OP_ERR) ||
status & BIT_ULL(CQ_OP_STAT_CQ_ERR))) {
dev_err(pfvf->dev, "CQ stopped due to error"); return -EINVAL;
}
if (!(pfvf->flags & OTX2_FLAG_RX_TSTAMP_ENABLED)) return;
timestamp = pfvf->ptp->convert_rx_ptp_tstmp(*(u64 *)data); /* The first 8 bytes is the timestamp */
err = otx2_ptp_tstamp2time(pfvf, timestamp, &tsns); if (err) return;
staticbool otx2_skb_add_frag(struct otx2_nic *pfvf, struct sk_buff *skb,
u64 iova, int len, struct nix_rx_parse_s *parse, int qidx)
{ struct page *page; int off = 0; void *va;
va = phys_to_virt(otx2_iova_to_phys(pfvf->iommu_domain, iova));
if (likely(!skb_shinfo(skb)->nr_frags)) { /* Check if data starts at some nonzero offset * from the start of the buffer. For now the * only possible offset is 8 bytes in the case * where packet is prepended by a timestamp.
*/ if (parse->laptr) {
otx2_set_rxtstamp(pfvf, skb, va);
off = OTX2_HW_TIMESTAMP_LEN;
}
}
page = virt_to_page(va); if (likely(skb_shinfo(skb)->nr_frags < MAX_SKB_FRAGS)) {
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
va - page_address(page) + off,
len - off, pfvf->rbsize); returntrue;
}
/* If more than MAX_SKB_FRAGS fragments are received then * give back those buffer pointers to hardware for reuse.
*/
pfvf->hw_ops->aura_freeptr(pfvf, qidx, iova & ~0x07ULL);
if (netif_msg_rx_err(pfvf))
netdev_err(pfvf->netdev, "RQ%d: Error pkt with errlev:0x%x errcode:0x%x\n",
qidx, parse->errlev, parse->errcode);
if (parse->errlev == NPC_ERRLVL_RE) { switch (parse->errcode) { case ERRCODE_FCS: case ERRCODE_FCS_RCV:
atomic_inc(&stats->rx_fcs_errs); break; case ERRCODE_UNDERSIZE:
atomic_inc(&stats->rx_undersize_errs); break; case ERRCODE_OVERSIZE:
atomic_inc(&stats->rx_oversize_errs); break; case ERRCODE_OL2_LEN_MISMATCH:
atomic_inc(&stats->rx_len_errs); break; default:
atomic_inc(&stats->rx_other_errs); break;
}
} elseif (parse->errlev == NPC_ERRLVL_NIX) { switch (parse->errcode) { case ERRCODE_OL3_LEN: case ERRCODE_OL4_LEN: case ERRCODE_IL3_LEN: case ERRCODE_IL4_LEN:
atomic_inc(&stats->rx_len_errs); break; case ERRCODE_OL4_CSUM: case ERRCODE_IL4_CSUM:
atomic_inc(&stats->rx_csum_errs); break; default:
atomic_inc(&stats->rx_other_errs); break;
}
} else {
atomic_inc(&stats->rx_other_errs); /* For now ignore all the NPC parser errors and * pass the packets to stack.
*/ returnfalse;
}
/* If RXALL is enabled pass on packets to stack. */ if (pfvf->netdev->features & NETIF_F_RXALL) returnfalse;
/* Free buffer back to pool */ if (cqe->sg.segs)
otx2_free_rcv_seg(pfvf, cqe, qidx); returntrue;
}
int otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq)
{ struct otx2_nic *pfvf = dev; int cnt = cq->pool_ptrs;
dma_addr_t bufptr;
while (cq->pool_ptrs) { if (otx2_alloc_buffer(pfvf, cq, &bufptr)) break;
otx2_aura_freeptr(pfvf, cq->cq_idx, bufptr + OTX2_HEAD_ROOM);
cq->pool_ptrs--;
}
return cnt - cq->pool_ptrs;
}
staticvoid otx2_zc_submit_pkts(struct otx2_nic *pfvf, struct xsk_buff_pool *xsk_pool, int *xsk_frames, int qidx, int budget)
{ if (*xsk_frames)
xsk_tx_completed(xsk_pool, *xsk_frames);
if (xsk_uses_need_wakeup(xsk_pool))
xsk_set_tx_need_wakeup(xsk_pool);
if (qidx >= pfvf->hw.tx_queues)
qidx -= pfvf->hw.xdp_queues; if (pfvf->flags & OTX2_FLAG_REP_MODE_ENABLED)
qidx = 0;
txq = netdev_get_tx_queue(ndev, qidx);
netdev_tx_completed_queue(txq, tx_pkts, tx_bytes); /* Check if queue was stopped earlier due to ring full */
smp_mb(); if (netif_tx_queue_stopped(txq) &&
netif_carrier_ok(ndev))
netif_tx_wake_queue(txq);
}
if (sq->xsk_pool)
otx2_zc_submit_pkts(pfvf, sq->xsk_pool, &xsk_frames, qidx, budget);
for (i = 0; i < CQS_PER_CINT; i++) {
cq_idx = cq_poll->cq_ids[i]; if (unlikely(cq_idx == CINT_INVALID_CQ)) continue;
cq = &qset->cq[cq_idx]; if (cq->cq_type == CQ_RX) {
rx_cq = cq;
workdone += otx2_rx_napi_handler(pfvf, napi,
cq, budget);
} else {
workdone += otx2_tx_napi_handler(pfvf, cq, budget);
}
}
if (rx_cq && rx_cq->pool_ptrs)
filled_cnt = pfvf->hw_ops->refill_pool_ptrs(pfvf, rx_cq);
/* Clear the IRQ */
otx2_write64(pfvf, NIX_LF_CINTX_INT(cq_poll->cint_idx), BIT_ULL(0));
if (workdone < budget && napi_complete_done(napi, workdone)) { /* If interface is going down, don't re-enable IRQ */ if (pfvf->flags & OTX2_FLAG_INTF_DOWN) return workdone;
/* Adjust irq coalese using net_dim */ if (pfvf->flags & OTX2_FLAG_ADPTV_INT_COAL_ENABLED)
otx2_adjust_adaptive_coalese(pfvf, cq_poll);
if (likely(cq))
pool = &pfvf->qset.pool[cq->cq_idx];
if (unlikely(!filled_cnt)) { struct refill_work *work; struct delayed_work *dwork;
if (likely(cq)) {
work = &pfvf->refill_wrk[cq->cq_idx];
dwork = &work->pool_refill_work; /* Schedule a task if no other task is running */ if (!cq->refill_task_sched) {
work->napi = napi;
cq->refill_task_sched = true;
schedule_delayed_work(dwork,
msecs_to_jiffies(100));
} /* Call wake-up for not able to fill buffers */ if (pool->xsk_pool)
xsk_set_rx_need_wakeup(pool->xsk_pool);
}
} else { /* Clear wake-up, since buffers are filled successfully */ if (pool && pool->xsk_pool)
xsk_clear_rx_need_wakeup(pool->xsk_pool); /* Re-enable interrupts */
otx2_write64(pfvf,
NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx),
BIT_ULL(0));
}
} return workdone;
}
EXPORT_SYMBOL(otx2_napi_handler);
void otx2_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx)
{
u64 status;
/* Packet data stores should finish before SQE is flushed to HW */
dma_wmb();
do {
memcpy(sq->lmt_addr, sq->sqe_base, size);
status = otx2_lmt_flush(sq->io_addr);
} while (status == 0);
/* Save DMA mapping info for later unmapping */
sq->sg[sq->head].dma_addr[seg] = dma_addr;
sq->sg[sq->head].size[seg] = len;
sq->sg[sq->head].num_segs++;
}
/* Check if SQE was framed before, if yes then no need to * set these constants again and again.
*/ if (!sqe_hdr->total) { /* Don't free Tx buffers to Aura */
sqe_hdr->df = 1;
sqe_hdr->aura = sq->aura_id; /* Post a CQE Tx after pkt transmission */
sqe_hdr->pnc = 1;
sqe_hdr->sq = (qidx >= pfvf->hw.tx_queues) ?
qidx + pfvf->hw.xdp_queues : qidx;
}
sqe_hdr->total = skb->len; /* Set SQE identifier which will be used later for freeing SKB */
sqe_hdr->sqe_id = sq->head;
/* Offload TCP/UDP checksum to HW */ if (skb->ip_summed == CHECKSUM_PARTIAL) {
sqe_hdr->ol3ptr = skb_network_offset(skb);
sqe_hdr->ol4ptr = skb_transport_offset(skb); /* get vlan protocol Ethertype */ if (eth_type_vlan(skb->protocol))
skb->protocol = vlan_get_protocol(skb);
if (skb->protocol == htons(ETH_P_IP)) {
proto = ip_hdr(skb)->protocol; /* In case of TSO, HW needs this to be explicitly set. * So set this always, instead of adding a check.
*/
sqe_hdr->ol3type = NIX_SENDL3TYPE_IP4_CKSUM;
} elseif (skb->protocol == htons(ETH_P_IPV6)) {
proto = ipv6_hdr(skb)->nexthdr;
sqe_hdr->ol3type = NIX_SENDL3TYPE_IP6;
}
staticint otx2_dma_map_tso_skb(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, struct sk_buff *skb, int sqe, int hdr_len)
{ int num_segs = skb_shinfo(skb)->nr_frags + 1; struct sg_list *sg = &sq->sg[sqe];
u64 dma_addr; int seg, len;
sg->num_segs = 0;
/* Get payload length at skb->data */
len = skb_headlen(skb) - hdr_len;
for (seg = 0; seg < num_segs; seg++) { /* Skip skb->data, if there is no payload */ if (!seg && !len) continue;
dma_addr = otx2_dma_map_skb_frag(pfvf, skb, seg, &len); if (dma_mapping_error(pfvf->dev, dma_addr)) goto unmap;
/* Save DMA mapping info for later unmapping */
sg->dma_addr[sg->num_segs] = dma_addr;
sg->size[sg->num_segs] = len;
sg->num_segs++;
} return 0;
unmap:
otx2_dma_unmap_skb_frags(pfvf, sg); return -EINVAL;
}
static u64 otx2_tso_frag_dma_addr(struct otx2_snd_queue *sq, struct sk_buff *skb, int seg,
u64 seg_addr, int hdr_len, int sqe)
{ struct sg_list *sg = &sq->sg[sqe]; const skb_frag_t *frag; int offset;
if (seg < 0) return sg->dma_addr[0] + (seg_addr - (u64)skb->data);
/* Map SKB's fragments to DMA. * It's done here to avoid mapping for every TSO segment's packet.
*/ if (otx2_dma_map_tso_skb(pfvf, sq, skb, first_sqe, hdr_len)) {
dev_kfree_skb_any(skb); return;
}
/* DMA mappings and skb needs to be freed only after last * TSO segment is transmitted out. So set 'PNC' only for * last segment. Also point last segment's sqe_id to first * segment's SQE index where skb address and DMA mappings * are saved.
*/ if (!tcp_data) {
sqe_hdr->pnc = 1;
sqe_hdr->sqe_id = first_sqe;
sq->sg[first_sqe].skb = (u64)skb;
} else {
sqe_hdr->pnc = 0;
}
if (test_bit(HW_TSO, &pfvf->hw.cap_flag)) returntrue;
/* On 96xx A0, HW TSO not supported */ if (!is_96xx_B0(pfvf->pdev)) returnfalse;
/* HW has an issue due to which when the payload of the last LSO * segment is shorter than 16 bytes, some header fields may not * be correctly modified, hence don't offload such TSO segments.
*/
/* NIX is programmed to offload outer VLAN header * in case of single vlan protocol field holds Network header ETH_IP/V6 * in case of stacked vlan protocol field holds Inner vlan (8100)
*/ if (skb->dev->features & NETIF_F_HW_VLAN_CTAG_TX &&
skb->dev->features & NETIF_F_HW_VLAN_STAG_TX) { if (skb->vlan_proto == htons(ETH_P_8021AD)) { /* Get vlan protocol */
proto = __vlan_get_protocol(skb, eth->h_proto, NULL); /* SKB APIs like skb_transport_offset does not include * offloaded vlan header length. Need to explicitly add * the length
*/
nix_offload_hlen = VLAN_HLEN;
inner_vhlen = VLAN_HLEN;
} elseif (skb->vlan_proto == htons(ETH_P_8021Q)) {
nix_offload_hlen = VLAN_HLEN;
}
} elseif (eth_type_vlan(eth->h_proto)) {
proto = __vlan_get_protocol(skb, eth->h_proto, &network_depth);
}
switch (ntohs(proto)) { case ETH_P_1588: if (network_depth)
*offset = network_depth; else
*offset = ETH_HLEN + nix_offload_hlen +
inner_vhlen; break; case ETH_P_IP: case ETH_P_IPV6: if (!otx2_validate_network_transport(skb)) returnfalse;
if (unlikely(!skb_shinfo(skb)->gso_size &&
(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) { if (unlikely(pfvf->flags & OTX2_FLAG_PTP_ONESTEP_SYNC &&
otx2_ptp_is_sync(skb, &ptp_offset, &udp_csum_crt))) {
origin_tstamp = (struct ptpv2_tstamp *)
((u8 *)skb->data + ptp_offset +
PTP_SYNC_SEC_OFFSET);
ts = ns_to_timespec64(pfvf->ptp->tstamp);
origin_tstamp->seconds_msb = htons((ts.tv_sec >> 32) & 0xffff);
origin_tstamp->seconds_lsb = htonl(ts.tv_sec & 0xffffffff);
origin_tstamp->nanoseconds = htonl(ts.tv_nsec); /* Point to correction field in PTP packet */
ptp_offset += 8;
/* When user disables hw checksum, stack calculates the csum, * but it does not cover ptp timestamp which is added later. * Recalculate the checksum manually considering the timestamp.
*/ if (udp_csum_crt) { struct udphdr *uh = udp_hdr(skb);
/* Check if there is enough room between producer * and consumer index.
*/
free_desc = otx2_get_free_sqe(sq); if (free_desc < sq->sqe_thresh) returnfalse;
if (free_desc < otx2_get_sqe_count(pfvf, skb)) returnfalse;
num_segs = skb_shinfo(skb)->nr_frags + 1;
/* If SKB doesn't fit in a single SQE, linearize it. * TODO: Consider adding JUMP descriptor instead.
*/
if (unlikely(num_segs > OTX2_MAX_FRAGS_IN_SQE)) { if (__skb_linearize(skb)) {
dev_kfree_skb_any(skb); returntrue;
}
num_segs = skb_shinfo(skb)->nr_frags + 1;
}
if (skb_shinfo(skb)->gso_size && !is_hw_tso_supported(pfvf, skb)) { /* Insert vlan tag before giving pkt to tso */ if (skb_vlan_tag_present(skb)) {
skb = __vlan_hwaccel_push_inside(skb); if (!skb) returntrue;
}
otx2_sq_append_tso(pfvf, sq, skb, qidx); returntrue;
}
/* Set sqe base address */
otx2_sq_set_sqe_base(sq, skb);
/* Set SQE's SEND_HDR. * Do not clear the first 64bit as it contains constant info.
*/
memset(sq->sqe_base + 8, 0, sq->sqe_size - 8);
sqe_hdr = (struct nix_sqe_hdr_s *)(sq->sqe_base);
otx2_sqe_add_hdr(pfvf, sq, sqe_hdr, skb, qidx);
offset = sizeof(*sqe_hdr);
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.