/* define outer network header type */ if (off->tx_flags & IDPF_TX_FLAGS_IPV4) { /* The stack computes the IP header already, the only * time we need the hardware to recompute it is in the * case of TSO.
*/
tunnel |= is_tso ?
IDPF_TX_CTX_EXT_IP_IPV4 :
IDPF_TX_CTX_EXT_IP_IPV4_NO_CSUM;
/* indicate if we need to offload outer UDP header */ if (is_tso &&
!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) &&
(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM))
tunnel |= IDPF_TXD_CTX_QW0_TUNN_L4T_CS_M;
/* record tunnel offload values */
off->cd_tunneling |= tunnel;
/* switch L4 header pointer from outer to inner */
l4.hdr = skb_inner_transport_header(skb);
l4_proto = 0;
/* reset type as we transition from outer to inner headers */
off->tx_flags &= ~(IDPF_TX_FLAGS_IPV4 | IDPF_TX_FLAGS_IPV6); if (ip.v4->version == 4)
off->tx_flags |= IDPF_TX_FLAGS_IPV4; if (ip.v6->version == 6)
off->tx_flags |= IDPF_TX_FLAGS_IPV6;
}
/* Enable IP checksum offloads */ if (off->tx_flags & IDPF_TX_FLAGS_IPV4) {
l4_proto = ip.v4->protocol; /* See comment above regarding need for HW to recompute IP * header checksum in the case of TSO.
*/ if (is_tso)
cmd |= IDPF_TX_DESC_CMD_IIPT_IPV4_CSUM; else
cmd |= IDPF_TX_DESC_CMD_IIPT_IPV4;
/* clear dma mappings for failed tx_buf map */ for (;;) { struct idpf_tx_buf *tx_buf;
tx_buf = &txq->tx_buf[idx];
libeth_tx_complete(tx_buf, &cp); if (tx_buf == first) break; if (idx == 0)
idx = txq->desc_count;
idx--;
}
if (skb_is_gso(skb)) { union idpf_tx_flex_desc *tx_desc;
/* If we failed a DMA mapping for a TSO packet, we will have * used one additional descriptor for a context * descriptor. Reset that here.
*/
tx_desc = &txq->flex_tx[idx];
memset(tx_desc, 0, sizeof(*tx_desc)); if (idx == 0)
idx = txq->desc_count;
idx--;
}
/* Update tail in case netdev_xmit_more was previously true */
idpf_tx_buf_hw_update(txq, idx, false);
}
/** * idpf_tx_singleq_map - Build the Tx base descriptor * @tx_q: queue to send buffer on * @first: first buffer info buffer to use * @offloads: pointer to struct that holds offload parameters * * This function loops over the skb data pointed to by *first * and gets a physical address for each memory location and programs * it and the length into the transmit base mode descriptor.
*/ staticvoid idpf_tx_singleq_map(struct idpf_tx_queue *tx_q, struct idpf_tx_buf *first, struct idpf_tx_offload_params *offloads)
{
u32 offsets = offloads->hdr_offsets; struct idpf_tx_buf *tx_buf = first; struct idpf_base_tx_desc *tx_desc; struct sk_buff *skb = first->skb;
u64 td_cmd = offloads->td_cmd; unsignedint data_len, size;
u16 i = tx_q->next_to_use; struct netdev_queue *nq;
skb_frag_t *frag;
dma_addr_t dma;
u64 td_tag = 0;
idpf_tx_buf_hw_update(tx_q, i, netdev_xmit_more());
}
/** * idpf_tx_singleq_get_ctx_desc - grab next desc and update buffer ring * @txq: queue to put context descriptor on * * Since the TX buffer rings mimics the descriptor ring, update the tx buffer * ring entry to reflect that this index is a context descriptor
*/ staticstruct idpf_base_tx_ctx_desc *
idpf_tx_singleq_get_ctx_desc(struct idpf_tx_queue *txq)
{ struct idpf_base_tx_ctx_desc *ctx_desc; int ntu = txq->next_to_use;
/* If this entry in the ring was used as a context descriptor, * it's corresponding entry in the buffer ring will indicate as * such. We can skip this descriptor since there is no buffer * to clean.
*/ if (unlikely(tx_buf->type <= LIBETH_SQE_CTX)) {
tx_buf->type = LIBETH_SQE_EMPTY; goto fetch_next_txq_desc;
}
if (unlikely(tx_buf->type != LIBETH_SQE_SKB)) break;
/* prevent any other reads prior to type */
smp_rmb();
eop_desc = &tx_q->base_tx[tx_buf->rs_idx];
/* if the descriptor isn't done, no work yet to do */ if (!(eop_desc->qw1 &
cpu_to_le64(IDPF_TX_DESC_DTYPE_DESC_DONE))) break;
/* update the statistics for this packet */
libeth_tx_complete(tx_buf, &cp);
/** * idpf_tx_singleq_clean_all - Clean all Tx queues * @q_vec: queue vector * @budget: Used to determine if we are in netpoll * @cleaned: returns number of packets cleaned * * Returns false if clean is not complete else returns true
*/ staticbool idpf_tx_singleq_clean_all(struct idpf_q_vector *q_vec, int budget, int *cleaned)
{
u16 num_txq = q_vec->num_txq; bool clean_complete = true; int i, budget_per_q;
budget_per_q = num_txq ? max(budget / num_txq, 1) : 0; for (i = 0; i < num_txq; i++) { struct idpf_tx_queue *q;
/** * idpf_rx_singleq_test_staterr - tests bits in Rx descriptor * status and error fields * @rx_desc: pointer to receive descriptor (in le64 format) * @stat_err_bits: value to mask * * This function does some fast chicanery in order to return the * value of the mask which is really only used for boolean tests. * The status_error_ptype_len doesn't need to be shifted because it begins * at offset zero.
*/ staticbool idpf_rx_singleq_test_staterr(constunion virtchnl2_rx_desc *rx_desc, const u64 stat_err_bits)
{ return !!(rx_desc->base_wb.qword1.status_error_ptype_len &
cpu_to_le64(stat_err_bits));
}
/** * idpf_rx_singleq_is_non_eop - process handling of non-EOP buffers * @rx_desc: Rx descriptor for current buffer
*/ staticbool idpf_rx_singleq_is_non_eop(constunion virtchnl2_rx_desc *rx_desc)
{ /* if we are the last buffer then there is nothing else to do */ if (likely(idpf_rx_singleq_test_staterr(rx_desc, IDPF_RXD_EOF_SINGLEQ))) returnfalse;
returntrue;
}
/** * idpf_rx_singleq_csum - Indicate in skb if checksum is good * @rxq: Rx ring being processed * @skb: skb currently being received and modified * @csum_bits: checksum bits from descriptor * @decoded: the packet type decoded by hardware * * skb->protocol must be set before this function is called
*/ staticvoid idpf_rx_singleq_csum(struct idpf_rx_queue *rxq, struct sk_buff *skb, struct libeth_rx_csum csum_bits, struct libeth_rx_pt decoded)
{ bool ipv4, ipv6;
/* check if Rx checksum is enabled */ if (!libeth_rx_pt_has_checksum(rxq->netdev, decoded)) return;
/* check if HW has decoded the packet and checksum */ if (unlikely(!csum_bits.l3l4p)) return;
/* Check if there were any checksum errors */ if (unlikely(ipv4 && (csum_bits.ipe || csum_bits.eipe))) goto checksum_fail;
/* Device could not do any checksum offload for certain extension * headers as indicated by setting IPV6EXADD bit
*/ if (unlikely(ipv6 && csum_bits.ipv6exadd)) return;
/* check for L4 errors and handle packets that were not able to be * checksummed due to arrival speed
*/ if (unlikely(csum_bits.l4e)) goto checksum_fail;
if (unlikely(csum_bits.nat && csum_bits.eudpe)) goto checksum_fail;
/* Handle packets that were not able to be checksummed due to arrival * speed, in this case the stack can compute the csum.
*/ if (unlikely(csum_bits.pprs)) return;
/* If there is an outer header present that might contain a checksum * we need to bump the checksum level by 1 to reflect the fact that * we are indicating we validated the inner checksum.
*/ if (decoded.tunnel_type >= LIBETH_RX_PT_TUNNEL_IP_GRENAT)
skb->csum_level = 1;
/** * idpf_rx_singleq_base_hash - set the hash value in the skb * @rx_q: Rx completion queue * @skb: skb currently being received and modified * @rx_desc: specific descriptor * @decoded: Decoded Rx packet type related fields * * This function only operates on the VIRTCHNL2_RXDID_1_32B_BASE_M base 32byte * descriptor writeback format.
**/ staticvoid idpf_rx_singleq_base_hash(struct idpf_rx_queue *rx_q, struct sk_buff *skb, constunion virtchnl2_rx_desc *rx_desc, struct libeth_rx_pt decoded)
{
u64 mask, qw1;
if (!libeth_rx_pt_has_hash(rx_q->netdev, decoded)) return;
if (FIELD_GET(mask, qw1) == mask) {
u32 hash = le32_to_cpu(rx_desc->base_wb.qword0.hi_dword.rss);
libeth_rx_pt_set_hash(skb, hash, decoded);
}
}
/** * idpf_rx_singleq_flex_hash - set the hash value in the skb * @rx_q: Rx completion queue * @skb: skb currently being received and modified * @rx_desc: specific descriptor * @decoded: Decoded Rx packet type related fields * * This function only operates on the VIRTCHNL2_RXDID_2_FLEX_SQ_NIC flexible * descriptor writeback format.
**/ staticvoid idpf_rx_singleq_flex_hash(struct idpf_rx_queue *rx_q, struct sk_buff *skb, constunion virtchnl2_rx_desc *rx_desc, struct libeth_rx_pt decoded)
{ if (!libeth_rx_pt_has_hash(rx_q->netdev, decoded)) return;
if (FIELD_GET(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_M,
le16_to_cpu(rx_desc->flex_nic_wb.status_error0))) {
u32 hash = le32_to_cpu(rx_desc->flex_nic_wb.rss_hash);
libeth_rx_pt_set_hash(skb, hash, decoded);
}
}
/** * idpf_rx_singleq_process_skb_fields - Populate skb header fields from Rx * descriptor * @rx_q: Rx ring being processed * @skb: pointer to current skb being populated * @rx_desc: descriptor for skb * @ptype: packet type * * This function checks the ring, descriptor, and packet information in * order to populate the hash, checksum, VLAN, protocol, and * other fields within the skb.
*/ staticvoid
idpf_rx_singleq_process_skb_fields(struct idpf_rx_queue *rx_q, struct sk_buff *skb, constunion virtchnl2_rx_desc *rx_desc,
u16 ptype)
{ struct libeth_rx_pt decoded = rx_q->rx_ptype_lkup[ptype]; struct libeth_rx_csum csum_bits;
/* modifies the skb - consumes the enet header */
skb->protocol = eth_type_trans(skb, rx_q->netdev);
/* Check if we're using base mode descriptor IDs */ if (rx_q->rxdids == VIRTCHNL2_RXDID_1_32B_BASE_M) {
idpf_rx_singleq_base_hash(rx_q, skb, rx_desc, decoded);
csum_bits = idpf_rx_singleq_base_csum(rx_desc);
} else {
idpf_rx_singleq_flex_hash(rx_q, skb, rx_desc, decoded);
csum_bits = idpf_rx_singleq_flex_csum(rx_desc);
}
/** * idpf_rx_buf_hw_update - Store the new tail and head values * @rxq: queue to bump * @val: new head index
*/ staticvoid idpf_rx_buf_hw_update(struct idpf_rx_queue *rxq, u32 val)
{
rxq->next_to_use = val;
if (unlikely(!rxq->tail)) return;
/* writel has an implicit memory barrier */
writel(val, rxq->tail);
}
/** * idpf_rx_singleq_buf_hw_alloc_all - Replace used receive buffers * @rx_q: queue for which the hw buffers are allocated * @cleaned_count: number of buffers to replace * * Returns false if all allocations were successful, true if any fail
*/ bool idpf_rx_singleq_buf_hw_alloc_all(struct idpf_rx_queue *rx_q,
u16 cleaned_count)
{ struct virtchnl2_singleq_rx_buf_desc *desc; conststruct libeth_fq_fp fq = {
.pp = rx_q->pp,
.fqes = rx_q->rx_buf,
.truesize = rx_q->truesize,
.count = rx_q->desc_count,
};
u16 nta = rx_q->next_to_alloc;
if (!cleaned_count) returnfalse;
desc = &rx_q->single_buf[nta];
do {
dma_addr_t addr;
addr = libeth_rx_alloc(&fq, nta); if (addr == DMA_MAPPING_ERROR) break;
/* Refresh the desc even if buffer_addrs didn't change * because each write-back erases this info.
*/
desc->pkt_addr = cpu_to_le64(addr);
desc->hdr_addr = 0;
desc++;
nta++; if (unlikely(nta == rx_q->desc_count)) {
desc = &rx_q->single_buf[0];
nta = 0;
}
/** * idpf_rx_singleq_extract_base_fields - Extract fields from the Rx descriptor * @rx_desc: the descriptor to process * @fields: storage for extracted values * * Decode the Rx descriptor and extract relevant information including the * size and Rx packet type. * * This function only operates on the VIRTCHNL2_RXDID_1_32B_BASE_M base 32byte * descriptor writeback format.
*/ staticvoid
idpf_rx_singleq_extract_base_fields(constunion virtchnl2_rx_desc *rx_desc, struct libeth_rqe_info *fields)
{
u64 qword;
/** * idpf_rx_singleq_extract_flex_fields - Extract fields from the Rx descriptor * @rx_desc: the descriptor to process * @fields: storage for extracted values * * Decode the Rx descriptor and extract relevant information including the * size and Rx packet type. * * This function only operates on the VIRTCHNL2_RXDID_2_FLEX_SQ_NIC flexible * descriptor writeback format.
*/ staticvoid
idpf_rx_singleq_extract_flex_fields(constunion virtchnl2_rx_desc *rx_desc, struct libeth_rqe_info *fields)
{
fields->len = FIELD_GET(VIRTCHNL2_RX_FLEX_DESC_PKT_LEN_M,
le16_to_cpu(rx_desc->flex_nic_wb.pkt_len));
fields->ptype = FIELD_GET(VIRTCHNL2_RX_FLEX_DESC_PTYPE_M,
le16_to_cpu(rx_desc->flex_nic_wb.ptype_flex_flags0));
}
/** * idpf_rx_singleq_extract_fields - Extract fields from the Rx descriptor * @rx_q: Rx descriptor queue * @rx_desc: the descriptor to process * @fields: storage for extracted values *
*/ staticvoid
idpf_rx_singleq_extract_fields(conststruct idpf_rx_queue *rx_q, constunion virtchnl2_rx_desc *rx_desc, struct libeth_rqe_info *fields)
{ if (rx_q->rxdids == VIRTCHNL2_RXDID_1_32B_BASE_M)
idpf_rx_singleq_extract_base_fields(rx_desc, fields); else
idpf_rx_singleq_extract_flex_fields(rx_desc, fields);
}
/** * idpf_rx_singleq_clean - Reclaim resources after receive completes * @rx_q: rx queue to clean * @budget: Total limit on number of packets to process * * Returns true if there's any budget left (e.g. the clean is finished)
*/ staticint idpf_rx_singleq_clean(struct idpf_rx_queue *rx_q, int budget)
{ unsignedint total_rx_bytes = 0, total_rx_pkts = 0; struct sk_buff *skb = rx_q->skb;
u16 ntc = rx_q->next_to_clean;
u16 cleaned_count = 0; bool failure = false;
/* Process Rx packets bounded by budget */ while (likely(total_rx_pkts < (unsignedint)budget)) { struct libeth_rqe_info fields = { }; union virtchnl2_rx_desc *rx_desc; struct idpf_rx_buf *rx_buf;
/* get the Rx desc from Rx queue based on 'next_to_clean' */
rx_desc = &rx_q->rx[ntc];
/* status_error_ptype_len will always be zero for unused * descriptors because it's cleared in cleanup, and overlaps * with hdr_addr which is always zero because packet split * isn't used, if the hardware wrote DD then the length will be * non-zero
*/ #define IDPF_RXD_DD VIRTCHNL2_RX_BASE_DESC_STATUS_DD_M if (!idpf_rx_singleq_test_staterr(rx_desc,
IDPF_RXD_DD)) break;
/* This memory barrier is needed to keep us from reading * any other fields out of the rx_desc
*/
dma_rmb();
/* guarantee a trip back through this routine if there was a failure */ return failure ? budget : (int)total_rx_pkts;
}
/** * idpf_rx_singleq_clean_all - Clean all Rx queues * @q_vec: queue vector * @budget: Used to determine if we are in netpoll * @cleaned: returns number of packets cleaned * * Returns false if clean is not complete else returns true
*/ staticbool idpf_rx_singleq_clean_all(struct idpf_q_vector *q_vec, int budget, int *cleaned)
{
u16 num_rxq = q_vec->num_rxq; bool clean_complete = true; int budget_per_q, i;
/* We attempt to distribute budget to each Rx queue fairly, but don't * allow the budget to go below 1 because that would exit polling early.
*/
budget_per_q = num_rxq ? max(budget / num_rxq, 1) : 0; for (i = 0; i < num_rxq; i++) { struct idpf_rx_queue *rxq = q_vec->rx[i]; int pkts_cleaned_per_q;
/* if we clean as many as budgeted, we must not be done */ if (pkts_cleaned_per_q >= budget_per_q)
clean_complete = false;
*cleaned += pkts_cleaned_per_q;
}
return clean_complete;
}
/** * idpf_vport_singleq_napi_poll - NAPI handler * @napi: struct from which you get q_vector * @budget: budget provided by stack
*/ int idpf_vport_singleq_napi_poll(struct napi_struct *napi, int budget)
{ struct idpf_q_vector *q_vector =
container_of(napi, struct idpf_q_vector, napi); bool clean_complete; int work_done = 0;
/* Handle case where we are called by netpoll with a budget of 0 */ if (budget <= 0) {
idpf_tx_singleq_clean_all(q_vector, budget, &work_done);
/* If work not completed, return budget and polling will return */ if (!clean_complete) {
idpf_vport_intr_set_wb_on_itr(q_vector); return budget;
}
work_done = min_t(int, work_done, budget - 1);
/* Exit the polling mode, but don't re-enable interrupts if stack might * poll us due to busy-polling
*/ if (likely(napi_complete_done(napi, work_done)))
idpf_vport_intr_update_itr_ena_irq(q_vector); else
idpf_vport_intr_set_wb_on_itr(q_vector);
return work_done;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet)
¤
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.