162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/ip.h> 562306a36Sopenharmony_ci#include <linux/ipv6.h> 662306a36Sopenharmony_ci#include <linux/if_vlan.h> 762306a36Sopenharmony_ci#include <net/ip6_checksum.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "ionic.h" 1062306a36Sopenharmony_ci#include "ionic_lif.h" 1162306a36Sopenharmony_ci#include "ionic_txrx.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell, 1462306a36Sopenharmony_ci ionic_desc_cb cb_func, void *cb_arg) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci ionic_q_post(q, ring_dbell, cb_func, cb_arg); 1762306a36Sopenharmony_ci} 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic inline void ionic_rxq_post(struct ionic_queue *q, bool ring_dbell, 2062306a36Sopenharmony_ci ionic_desc_cb cb_func, void *cb_arg) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci ionic_q_post(q, ring_dbell, cb_func, cb_arg); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cibool ionic_txq_poke_doorbell(struct ionic_queue *q) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci unsigned long now, then, dif; 2862306a36Sopenharmony_ci struct netdev_queue *netdev_txq; 2962306a36Sopenharmony_ci struct net_device *netdev; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci netdev = q->lif->netdev; 3262306a36Sopenharmony_ci netdev_txq = netdev_get_tx_queue(netdev, q->index); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci HARD_TX_LOCK(netdev, netdev_txq, smp_processor_id()); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (q->tail_idx == q->head_idx) { 3762306a36Sopenharmony_ci HARD_TX_UNLOCK(netdev, netdev_txq); 3862306a36Sopenharmony_ci return false; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci now = READ_ONCE(jiffies); 4262306a36Sopenharmony_ci then = q->dbell_jiffies; 4362306a36Sopenharmony_ci dif = now - then; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (dif > q->dbell_deadline) { 4662306a36Sopenharmony_ci ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, 4762306a36Sopenharmony_ci q->dbval | q->head_idx); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci q->dbell_jiffies = now; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci HARD_TX_UNLOCK(netdev, netdev_txq); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return true; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cibool ionic_rxq_poke_doorbell(struct ionic_queue *q) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci unsigned long now, then, dif; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* no lock, called from rx napi or txrx napi, nothing else can fill */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (q->tail_idx == q->head_idx) 6462306a36Sopenharmony_ci return false; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci now = READ_ONCE(jiffies); 6762306a36Sopenharmony_ci then = q->dbell_jiffies; 6862306a36Sopenharmony_ci dif = now - then; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (dif > q->dbell_deadline) { 7162306a36Sopenharmony_ci ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, 7262306a36Sopenharmony_ci q->dbval | q->head_idx); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci q->dbell_jiffies = now; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci dif = 2 * q->dbell_deadline; 7762306a36Sopenharmony_ci if (dif > IONIC_RX_MAX_DOORBELL_DEADLINE) 7862306a36Sopenharmony_ci dif = IONIC_RX_MAX_DOORBELL_DEADLINE; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci q->dbell_deadline = dif; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return true; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic inline struct netdev_queue *q_to_ndq(struct ionic_queue *q) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return netdev_get_tx_queue(q->lif->netdev, q->index); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int ionic_rx_page_alloc(struct ionic_queue *q, 9262306a36Sopenharmony_ci struct ionic_buf_info *buf_info) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct net_device *netdev = q->lif->netdev; 9562306a36Sopenharmony_ci struct ionic_rx_stats *stats; 9662306a36Sopenharmony_ci struct device *dev; 9762306a36Sopenharmony_ci struct page *page; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci dev = q->dev; 10062306a36Sopenharmony_ci stats = q_to_rx_stats(q); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (unlikely(!buf_info)) { 10362306a36Sopenharmony_ci net_err_ratelimited("%s: %s invalid buf_info in alloc\n", 10462306a36Sopenharmony_ci netdev->name, q->name); 10562306a36Sopenharmony_ci return -EINVAL; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci page = alloc_pages(IONIC_PAGE_GFP_MASK, 0); 10962306a36Sopenharmony_ci if (unlikely(!page)) { 11062306a36Sopenharmony_ci net_err_ratelimited("%s: %s page alloc failed\n", 11162306a36Sopenharmony_ci netdev->name, q->name); 11262306a36Sopenharmony_ci stats->alloc_err++; 11362306a36Sopenharmony_ci return -ENOMEM; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci buf_info->dma_addr = dma_map_page(dev, page, 0, 11762306a36Sopenharmony_ci IONIC_PAGE_SIZE, DMA_FROM_DEVICE); 11862306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, buf_info->dma_addr))) { 11962306a36Sopenharmony_ci __free_pages(page, 0); 12062306a36Sopenharmony_ci net_err_ratelimited("%s: %s dma map failed\n", 12162306a36Sopenharmony_ci netdev->name, q->name); 12262306a36Sopenharmony_ci stats->dma_map_err++; 12362306a36Sopenharmony_ci return -EIO; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci buf_info->page = page; 12762306a36Sopenharmony_ci buf_info->page_offset = 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void ionic_rx_page_free(struct ionic_queue *q, 13362306a36Sopenharmony_ci struct ionic_buf_info *buf_info) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct net_device *netdev = q->lif->netdev; 13662306a36Sopenharmony_ci struct device *dev = q->dev; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (unlikely(!buf_info)) { 13962306a36Sopenharmony_ci net_err_ratelimited("%s: %s invalid buf_info in free\n", 14062306a36Sopenharmony_ci netdev->name, q->name); 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (!buf_info->page) 14562306a36Sopenharmony_ci return; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dma_unmap_page(dev, buf_info->dma_addr, IONIC_PAGE_SIZE, DMA_FROM_DEVICE); 14862306a36Sopenharmony_ci __free_pages(buf_info->page, 0); 14962306a36Sopenharmony_ci buf_info->page = NULL; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic bool ionic_rx_buf_recycle(struct ionic_queue *q, 15362306a36Sopenharmony_ci struct ionic_buf_info *buf_info, u32 used) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci u32 size; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* don't re-use pages allocated in low-mem condition */ 15862306a36Sopenharmony_ci if (page_is_pfmemalloc(buf_info->page)) 15962306a36Sopenharmony_ci return false; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* don't re-use buffers from non-local numa nodes */ 16262306a36Sopenharmony_ci if (page_to_nid(buf_info->page) != numa_mem_id()) 16362306a36Sopenharmony_ci return false; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci size = ALIGN(used, IONIC_PAGE_SPLIT_SZ); 16662306a36Sopenharmony_ci buf_info->page_offset += size; 16762306a36Sopenharmony_ci if (buf_info->page_offset >= IONIC_PAGE_SIZE) 16862306a36Sopenharmony_ci return false; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci get_page(buf_info->page); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return true; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic struct sk_buff *ionic_rx_frags(struct ionic_queue *q, 17662306a36Sopenharmony_ci struct ionic_desc_info *desc_info, 17762306a36Sopenharmony_ci struct ionic_rxq_comp *comp) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct net_device *netdev = q->lif->netdev; 18062306a36Sopenharmony_ci struct ionic_buf_info *buf_info; 18162306a36Sopenharmony_ci struct ionic_rx_stats *stats; 18262306a36Sopenharmony_ci struct device *dev = q->dev; 18362306a36Sopenharmony_ci struct sk_buff *skb; 18462306a36Sopenharmony_ci unsigned int i; 18562306a36Sopenharmony_ci u16 frag_len; 18662306a36Sopenharmony_ci u16 len; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci stats = q_to_rx_stats(q); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci buf_info = &desc_info->bufs[0]; 19162306a36Sopenharmony_ci len = le16_to_cpu(comp->len); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci prefetchw(buf_info->page); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci skb = napi_get_frags(&q_to_qcq(q)->napi); 19662306a36Sopenharmony_ci if (unlikely(!skb)) { 19762306a36Sopenharmony_ci net_warn_ratelimited("%s: SKB alloc failed on %s!\n", 19862306a36Sopenharmony_ci netdev->name, q->name); 19962306a36Sopenharmony_ci stats->alloc_err++; 20062306a36Sopenharmony_ci return NULL; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci i = comp->num_sg_elems + 1; 20462306a36Sopenharmony_ci do { 20562306a36Sopenharmony_ci if (unlikely(!buf_info->page)) { 20662306a36Sopenharmony_ci dev_kfree_skb(skb); 20762306a36Sopenharmony_ci return NULL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci frag_len = min_t(u16, len, min_t(u32, IONIC_MAX_BUF_LEN, 21162306a36Sopenharmony_ci IONIC_PAGE_SIZE - buf_info->page_offset)); 21262306a36Sopenharmony_ci len -= frag_len; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dma_sync_single_for_cpu(dev, 21562306a36Sopenharmony_ci buf_info->dma_addr + buf_info->page_offset, 21662306a36Sopenharmony_ci frag_len, DMA_FROM_DEVICE); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 21962306a36Sopenharmony_ci buf_info->page, buf_info->page_offset, frag_len, 22062306a36Sopenharmony_ci IONIC_PAGE_SIZE); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!ionic_rx_buf_recycle(q, buf_info, frag_len)) { 22362306a36Sopenharmony_ci dma_unmap_page(dev, buf_info->dma_addr, 22462306a36Sopenharmony_ci IONIC_PAGE_SIZE, DMA_FROM_DEVICE); 22562306a36Sopenharmony_ci buf_info->page = NULL; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci buf_info++; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci i--; 23162306a36Sopenharmony_ci } while (i > 0); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return skb; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, 23762306a36Sopenharmony_ci struct ionic_desc_info *desc_info, 23862306a36Sopenharmony_ci struct ionic_rxq_comp *comp) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct net_device *netdev = q->lif->netdev; 24162306a36Sopenharmony_ci struct ionic_buf_info *buf_info; 24262306a36Sopenharmony_ci struct ionic_rx_stats *stats; 24362306a36Sopenharmony_ci struct device *dev = q->dev; 24462306a36Sopenharmony_ci struct sk_buff *skb; 24562306a36Sopenharmony_ci u16 len; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci stats = q_to_rx_stats(q); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci buf_info = &desc_info->bufs[0]; 25062306a36Sopenharmony_ci len = le16_to_cpu(comp->len); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci skb = napi_alloc_skb(&q_to_qcq(q)->napi, len); 25362306a36Sopenharmony_ci if (unlikely(!skb)) { 25462306a36Sopenharmony_ci net_warn_ratelimited("%s: SKB alloc failed on %s!\n", 25562306a36Sopenharmony_ci netdev->name, q->name); 25662306a36Sopenharmony_ci stats->alloc_err++; 25762306a36Sopenharmony_ci return NULL; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (unlikely(!buf_info->page)) { 26162306a36Sopenharmony_ci dev_kfree_skb(skb); 26262306a36Sopenharmony_ci return NULL; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci dma_sync_single_for_cpu(dev, buf_info->dma_addr + buf_info->page_offset, 26662306a36Sopenharmony_ci len, DMA_FROM_DEVICE); 26762306a36Sopenharmony_ci skb_copy_to_linear_data(skb, page_address(buf_info->page) + buf_info->page_offset, len); 26862306a36Sopenharmony_ci dma_sync_single_for_device(dev, buf_info->dma_addr + buf_info->page_offset, 26962306a36Sopenharmony_ci len, DMA_FROM_DEVICE); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci skb_put(skb, len); 27262306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, q->lif->netdev); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return skb; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void ionic_rx_clean(struct ionic_queue *q, 27862306a36Sopenharmony_ci struct ionic_desc_info *desc_info, 27962306a36Sopenharmony_ci struct ionic_cq_info *cq_info, 28062306a36Sopenharmony_ci void *cb_arg) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct net_device *netdev = q->lif->netdev; 28362306a36Sopenharmony_ci struct ionic_qcq *qcq = q_to_qcq(q); 28462306a36Sopenharmony_ci struct ionic_rx_stats *stats; 28562306a36Sopenharmony_ci struct ionic_rxq_comp *comp; 28662306a36Sopenharmony_ci struct sk_buff *skb; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci comp = cq_info->cq_desc + qcq->cq.desc_size - sizeof(*comp); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci stats = q_to_rx_stats(q); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (comp->status) { 29362306a36Sopenharmony_ci stats->dropped++; 29462306a36Sopenharmony_ci return; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci stats->pkts++; 29862306a36Sopenharmony_ci stats->bytes += le16_to_cpu(comp->len); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) 30162306a36Sopenharmony_ci skb = ionic_rx_copybreak(q, desc_info, comp); 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci skb = ionic_rx_frags(q, desc_info, comp); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (unlikely(!skb)) { 30662306a36Sopenharmony_ci stats->dropped++; 30762306a36Sopenharmony_ci return; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci skb_record_rx_queue(skb, q->index); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (likely(netdev->features & NETIF_F_RXHASH)) { 31362306a36Sopenharmony_ci switch (comp->pkt_type_color & IONIC_RXQ_COMP_PKT_TYPE_MASK) { 31462306a36Sopenharmony_ci case IONIC_PKT_TYPE_IPV4: 31562306a36Sopenharmony_ci case IONIC_PKT_TYPE_IPV6: 31662306a36Sopenharmony_ci skb_set_hash(skb, le32_to_cpu(comp->rss_hash), 31762306a36Sopenharmony_ci PKT_HASH_TYPE_L3); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case IONIC_PKT_TYPE_IPV4_TCP: 32062306a36Sopenharmony_ci case IONIC_PKT_TYPE_IPV6_TCP: 32162306a36Sopenharmony_ci case IONIC_PKT_TYPE_IPV4_UDP: 32262306a36Sopenharmony_ci case IONIC_PKT_TYPE_IPV6_UDP: 32362306a36Sopenharmony_ci skb_set_hash(skb, le32_to_cpu(comp->rss_hash), 32462306a36Sopenharmony_ci PKT_HASH_TYPE_L4); 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (likely(netdev->features & NETIF_F_RXCSUM) && 33062306a36Sopenharmony_ci (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_CALC)) { 33162306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_COMPLETE; 33262306a36Sopenharmony_ci skb->csum = (__force __wsum)le16_to_cpu(comp->csum); 33362306a36Sopenharmony_ci stats->csum_complete++; 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci stats->csum_none++; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (unlikely((comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_TCP_BAD) || 33962306a36Sopenharmony_ci (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_UDP_BAD) || 34062306a36Sopenharmony_ci (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_IP_BAD))) 34162306a36Sopenharmony_ci stats->csum_error++; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (likely(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && 34462306a36Sopenharmony_ci (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_VLAN)) { 34562306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), 34662306a36Sopenharmony_ci le16_to_cpu(comp->vlan_tci)); 34762306a36Sopenharmony_ci stats->vlan_stripped++; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (unlikely(q->features & IONIC_RXQ_F_HWSTAMP)) { 35162306a36Sopenharmony_ci __le64 *cq_desc_hwstamp; 35262306a36Sopenharmony_ci u64 hwstamp; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci cq_desc_hwstamp = 35562306a36Sopenharmony_ci cq_info->cq_desc + 35662306a36Sopenharmony_ci qcq->cq.desc_size - 35762306a36Sopenharmony_ci sizeof(struct ionic_rxq_comp) - 35862306a36Sopenharmony_ci IONIC_HWSTAMP_CQ_NEGOFFSET; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci hwstamp = le64_to_cpu(*cq_desc_hwstamp); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (hwstamp != IONIC_HWSTAMP_INVALID) { 36362306a36Sopenharmony_ci skb_hwtstamps(skb)->hwtstamp = ionic_lif_phc_ktime(q->lif, hwstamp); 36462306a36Sopenharmony_ci stats->hwstamp_valid++; 36562306a36Sopenharmony_ci } else { 36662306a36Sopenharmony_ci stats->hwstamp_invalid++; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) 37162306a36Sopenharmony_ci napi_gro_receive(&qcq->napi, skb); 37262306a36Sopenharmony_ci else 37362306a36Sopenharmony_ci napi_gro_frags(&qcq->napi); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cibool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct ionic_queue *q = cq->bound_q; 37962306a36Sopenharmony_ci struct ionic_desc_info *desc_info; 38062306a36Sopenharmony_ci struct ionic_rxq_comp *comp; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci comp = cq_info->cq_desc + cq->desc_size - sizeof(*comp); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (!color_match(comp->pkt_type_color, cq->done_color)) 38562306a36Sopenharmony_ci return false; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* check for empty queue */ 38862306a36Sopenharmony_ci if (q->tail_idx == q->head_idx) 38962306a36Sopenharmony_ci return false; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (q->tail_idx != le16_to_cpu(comp->comp_index)) 39262306a36Sopenharmony_ci return false; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci desc_info = &q->info[q->tail_idx]; 39562306a36Sopenharmony_ci q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* clean the related q entry, only one per qc completion */ 39862306a36Sopenharmony_ci ionic_rx_clean(q, desc_info, cq_info, desc_info->cb_arg); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci desc_info->cb = NULL; 40162306a36Sopenharmony_ci desc_info->cb_arg = NULL; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return true; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic inline void ionic_write_cmb_desc(struct ionic_queue *q, 40762306a36Sopenharmony_ci void __iomem *cmb_desc, 40862306a36Sopenharmony_ci void *desc) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci if (q_to_qcq(q)->flags & IONIC_QCQ_F_CMB_RINGS) 41162306a36Sopenharmony_ci memcpy_toio(cmb_desc, desc, q->desc_size); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_civoid ionic_rx_fill(struct ionic_queue *q) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct net_device *netdev = q->lif->netdev; 41762306a36Sopenharmony_ci struct ionic_desc_info *desc_info; 41862306a36Sopenharmony_ci struct ionic_rxq_sg_desc *sg_desc; 41962306a36Sopenharmony_ci struct ionic_rxq_sg_elem *sg_elem; 42062306a36Sopenharmony_ci struct ionic_buf_info *buf_info; 42162306a36Sopenharmony_ci unsigned int fill_threshold; 42262306a36Sopenharmony_ci struct ionic_rxq_desc *desc; 42362306a36Sopenharmony_ci unsigned int remain_len; 42462306a36Sopenharmony_ci unsigned int frag_len; 42562306a36Sopenharmony_ci unsigned int nfrags; 42662306a36Sopenharmony_ci unsigned int n_fill; 42762306a36Sopenharmony_ci unsigned int i, j; 42862306a36Sopenharmony_ci unsigned int len; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci n_fill = ionic_q_space_avail(q); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci fill_threshold = min_t(unsigned int, IONIC_RX_FILL_THRESHOLD, 43362306a36Sopenharmony_ci q->num_descs / IONIC_RX_FILL_DIV); 43462306a36Sopenharmony_ci if (n_fill < fill_threshold) 43562306a36Sopenharmony_ci return; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci len = netdev->mtu + ETH_HLEN + VLAN_HLEN; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci for (i = n_fill; i; i--) { 44062306a36Sopenharmony_ci nfrags = 0; 44162306a36Sopenharmony_ci remain_len = len; 44262306a36Sopenharmony_ci desc_info = &q->info[q->head_idx]; 44362306a36Sopenharmony_ci desc = desc_info->desc; 44462306a36Sopenharmony_ci buf_info = &desc_info->bufs[0]; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!buf_info->page) { /* alloc a new buffer? */ 44762306a36Sopenharmony_ci if (unlikely(ionic_rx_page_alloc(q, buf_info))) { 44862306a36Sopenharmony_ci desc->addr = 0; 44962306a36Sopenharmony_ci desc->len = 0; 45062306a36Sopenharmony_ci return; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* fill main descriptor - buf[0] */ 45562306a36Sopenharmony_ci desc->addr = cpu_to_le64(buf_info->dma_addr + buf_info->page_offset); 45662306a36Sopenharmony_ci frag_len = min_t(u16, len, min_t(u32, IONIC_MAX_BUF_LEN, 45762306a36Sopenharmony_ci IONIC_PAGE_SIZE - buf_info->page_offset)); 45862306a36Sopenharmony_ci desc->len = cpu_to_le16(frag_len); 45962306a36Sopenharmony_ci remain_len -= frag_len; 46062306a36Sopenharmony_ci buf_info++; 46162306a36Sopenharmony_ci nfrags++; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* fill sg descriptors - buf[1..n] */ 46462306a36Sopenharmony_ci sg_desc = desc_info->sg_desc; 46562306a36Sopenharmony_ci for (j = 0; remain_len > 0 && j < q->max_sg_elems; j++) { 46662306a36Sopenharmony_ci sg_elem = &sg_desc->elems[j]; 46762306a36Sopenharmony_ci if (!buf_info->page) { /* alloc a new sg buffer? */ 46862306a36Sopenharmony_ci if (unlikely(ionic_rx_page_alloc(q, buf_info))) { 46962306a36Sopenharmony_ci sg_elem->addr = 0; 47062306a36Sopenharmony_ci sg_elem->len = 0; 47162306a36Sopenharmony_ci return; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci sg_elem->addr = cpu_to_le64(buf_info->dma_addr + buf_info->page_offset); 47662306a36Sopenharmony_ci frag_len = min_t(u16, remain_len, min_t(u32, IONIC_MAX_BUF_LEN, 47762306a36Sopenharmony_ci IONIC_PAGE_SIZE - 47862306a36Sopenharmony_ci buf_info->page_offset)); 47962306a36Sopenharmony_ci sg_elem->len = cpu_to_le16(frag_len); 48062306a36Sopenharmony_ci remain_len -= frag_len; 48162306a36Sopenharmony_ci buf_info++; 48262306a36Sopenharmony_ci nfrags++; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* clear end sg element as a sentinel */ 48662306a36Sopenharmony_ci if (j < q->max_sg_elems) { 48762306a36Sopenharmony_ci sg_elem = &sg_desc->elems[j]; 48862306a36Sopenharmony_ci memset(sg_elem, 0, sizeof(*sg_elem)); 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci desc->opcode = (nfrags > 1) ? IONIC_RXQ_DESC_OPCODE_SG : 49262306a36Sopenharmony_ci IONIC_RXQ_DESC_OPCODE_SIMPLE; 49362306a36Sopenharmony_ci desc_info->nbufs = nfrags; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ionic_write_cmb_desc(q, desc_info->cmb_desc, desc); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ionic_rxq_post(q, false, ionic_rx_clean, NULL); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, 50162306a36Sopenharmony_ci q->dbval | q->head_idx); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci q->dbell_deadline = IONIC_RX_MIN_DOORBELL_DEADLINE; 50462306a36Sopenharmony_ci q->dbell_jiffies = jiffies; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci mod_timer(&q_to_qcq(q)->napi_qcq->napi_deadline, 50762306a36Sopenharmony_ci jiffies + IONIC_NAPI_DEADLINE); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_civoid ionic_rx_empty(struct ionic_queue *q) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct ionic_desc_info *desc_info; 51362306a36Sopenharmony_ci struct ionic_buf_info *buf_info; 51462306a36Sopenharmony_ci unsigned int i, j; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci for (i = 0; i < q->num_descs; i++) { 51762306a36Sopenharmony_ci desc_info = &q->info[i]; 51862306a36Sopenharmony_ci for (j = 0; j < IONIC_RX_MAX_SG_ELEMS + 1; j++) { 51962306a36Sopenharmony_ci buf_info = &desc_info->bufs[j]; 52062306a36Sopenharmony_ci if (buf_info->page) 52162306a36Sopenharmony_ci ionic_rx_page_free(q, buf_info); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci desc_info->nbufs = 0; 52562306a36Sopenharmony_ci desc_info->cb = NULL; 52662306a36Sopenharmony_ci desc_info->cb_arg = NULL; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci q->head_idx = 0; 53062306a36Sopenharmony_ci q->tail_idx = 0; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void ionic_dim_update(struct ionic_qcq *qcq, int napi_mode) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct dim_sample dim_sample; 53662306a36Sopenharmony_ci struct ionic_lif *lif; 53762306a36Sopenharmony_ci unsigned int qi; 53862306a36Sopenharmony_ci u64 pkts, bytes; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!qcq->intr.dim_coal_hw) 54162306a36Sopenharmony_ci return; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci lif = qcq->q.lif; 54462306a36Sopenharmony_ci qi = qcq->cq.bound_q->index; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci switch (napi_mode) { 54762306a36Sopenharmony_ci case IONIC_LIF_F_TX_DIM_INTR: 54862306a36Sopenharmony_ci pkts = lif->txqstats[qi].pkts; 54962306a36Sopenharmony_ci bytes = lif->txqstats[qi].bytes; 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci case IONIC_LIF_F_RX_DIM_INTR: 55262306a36Sopenharmony_ci pkts = lif->rxqstats[qi].pkts; 55362306a36Sopenharmony_ci bytes = lif->rxqstats[qi].bytes; 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci default: 55662306a36Sopenharmony_ci pkts = lif->txqstats[qi].pkts + lif->rxqstats[qi].pkts; 55762306a36Sopenharmony_ci bytes = lif->txqstats[qi].bytes + lif->rxqstats[qi].bytes; 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci dim_update_sample(qcq->cq.bound_intr->rearm_count, 56262306a36Sopenharmony_ci pkts, bytes, &dim_sample); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci net_dim(&qcq->dim, dim_sample); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciint ionic_tx_napi(struct napi_struct *napi, int budget) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct ionic_qcq *qcq = napi_to_qcq(napi); 57062306a36Sopenharmony_ci struct ionic_cq *cq = napi_to_cq(napi); 57162306a36Sopenharmony_ci struct ionic_dev *idev; 57262306a36Sopenharmony_ci struct ionic_lif *lif; 57362306a36Sopenharmony_ci u32 work_done = 0; 57462306a36Sopenharmony_ci u32 flags = 0; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci lif = cq->bound_q->lif; 57762306a36Sopenharmony_ci idev = &lif->ionic->idev; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci work_done = ionic_cq_service(cq, budget, 58062306a36Sopenharmony_ci ionic_tx_service, NULL, NULL); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (work_done < budget && napi_complete_done(napi, work_done)) { 58362306a36Sopenharmony_ci ionic_dim_update(qcq, IONIC_LIF_F_TX_DIM_INTR); 58462306a36Sopenharmony_ci flags |= IONIC_INTR_CRED_UNMASK; 58562306a36Sopenharmony_ci cq->bound_intr->rearm_count++; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (work_done || flags) { 58962306a36Sopenharmony_ci flags |= IONIC_INTR_CRED_RESET_COALESCE; 59062306a36Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, 59162306a36Sopenharmony_ci cq->bound_intr->index, 59262306a36Sopenharmony_ci work_done, flags); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (!work_done && ionic_txq_poke_doorbell(&qcq->q)) 59662306a36Sopenharmony_ci mod_timer(&qcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return work_done; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ciint ionic_rx_napi(struct napi_struct *napi, int budget) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct ionic_qcq *qcq = napi_to_qcq(napi); 60462306a36Sopenharmony_ci struct ionic_cq *cq = napi_to_cq(napi); 60562306a36Sopenharmony_ci struct ionic_dev *idev; 60662306a36Sopenharmony_ci struct ionic_lif *lif; 60762306a36Sopenharmony_ci u32 work_done = 0; 60862306a36Sopenharmony_ci u32 flags = 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci lif = cq->bound_q->lif; 61162306a36Sopenharmony_ci idev = &lif->ionic->idev; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci work_done = ionic_cq_service(cq, budget, 61462306a36Sopenharmony_ci ionic_rx_service, NULL, NULL); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci ionic_rx_fill(cq->bound_q); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (work_done < budget && napi_complete_done(napi, work_done)) { 61962306a36Sopenharmony_ci ionic_dim_update(qcq, IONIC_LIF_F_RX_DIM_INTR); 62062306a36Sopenharmony_ci flags |= IONIC_INTR_CRED_UNMASK; 62162306a36Sopenharmony_ci cq->bound_intr->rearm_count++; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (work_done || flags) { 62562306a36Sopenharmony_ci flags |= IONIC_INTR_CRED_RESET_COALESCE; 62662306a36Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, 62762306a36Sopenharmony_ci cq->bound_intr->index, 62862306a36Sopenharmony_ci work_done, flags); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (!work_done && ionic_rxq_poke_doorbell(&qcq->q)) 63262306a36Sopenharmony_ci mod_timer(&qcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return work_done; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ciint ionic_txrx_napi(struct napi_struct *napi, int budget) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct ionic_qcq *rxqcq = napi_to_qcq(napi); 64062306a36Sopenharmony_ci struct ionic_cq *rxcq = napi_to_cq(napi); 64162306a36Sopenharmony_ci unsigned int qi = rxcq->bound_q->index; 64262306a36Sopenharmony_ci struct ionic_qcq *txqcq; 64362306a36Sopenharmony_ci struct ionic_dev *idev; 64462306a36Sopenharmony_ci struct ionic_lif *lif; 64562306a36Sopenharmony_ci struct ionic_cq *txcq; 64662306a36Sopenharmony_ci bool resched = false; 64762306a36Sopenharmony_ci u32 rx_work_done = 0; 64862306a36Sopenharmony_ci u32 tx_work_done = 0; 64962306a36Sopenharmony_ci u32 flags = 0; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci lif = rxcq->bound_q->lif; 65262306a36Sopenharmony_ci idev = &lif->ionic->idev; 65362306a36Sopenharmony_ci txqcq = lif->txqcqs[qi]; 65462306a36Sopenharmony_ci txcq = &lif->txqcqs[qi]->cq; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci tx_work_done = ionic_cq_service(txcq, IONIC_TX_BUDGET_DEFAULT, 65762306a36Sopenharmony_ci ionic_tx_service, NULL, NULL); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci rx_work_done = ionic_cq_service(rxcq, budget, 66062306a36Sopenharmony_ci ionic_rx_service, NULL, NULL); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci ionic_rx_fill(rxcq->bound_q); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (rx_work_done < budget && napi_complete_done(napi, rx_work_done)) { 66562306a36Sopenharmony_ci ionic_dim_update(rxqcq, 0); 66662306a36Sopenharmony_ci flags |= IONIC_INTR_CRED_UNMASK; 66762306a36Sopenharmony_ci rxcq->bound_intr->rearm_count++; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (rx_work_done || flags) { 67162306a36Sopenharmony_ci flags |= IONIC_INTR_CRED_RESET_COALESCE; 67262306a36Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, rxcq->bound_intr->index, 67362306a36Sopenharmony_ci tx_work_done + rx_work_done, flags); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (!rx_work_done && ionic_rxq_poke_doorbell(&rxqcq->q)) 67762306a36Sopenharmony_ci resched = true; 67862306a36Sopenharmony_ci if (!tx_work_done && ionic_txq_poke_doorbell(&txqcq->q)) 67962306a36Sopenharmony_ci resched = true; 68062306a36Sopenharmony_ci if (resched) 68162306a36Sopenharmony_ci mod_timer(&rxqcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return rx_work_done; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic dma_addr_t ionic_tx_map_single(struct ionic_queue *q, 68762306a36Sopenharmony_ci void *data, size_t len) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 69062306a36Sopenharmony_ci struct device *dev = q->dev; 69162306a36Sopenharmony_ci dma_addr_t dma_addr; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci dma_addr = dma_map_single(dev, data, len, DMA_TO_DEVICE); 69462306a36Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) { 69562306a36Sopenharmony_ci net_warn_ratelimited("%s: DMA single map failed on %s!\n", 69662306a36Sopenharmony_ci q->lif->netdev->name, q->name); 69762306a36Sopenharmony_ci stats->dma_map_err++; 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci return dma_addr; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic dma_addr_t ionic_tx_map_frag(struct ionic_queue *q, 70462306a36Sopenharmony_ci const skb_frag_t *frag, 70562306a36Sopenharmony_ci size_t offset, size_t len) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 70862306a36Sopenharmony_ci struct device *dev = q->dev; 70962306a36Sopenharmony_ci dma_addr_t dma_addr; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci dma_addr = skb_frag_dma_map(dev, frag, offset, len, DMA_TO_DEVICE); 71262306a36Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) { 71362306a36Sopenharmony_ci net_warn_ratelimited("%s: DMA frag map failed on %s!\n", 71462306a36Sopenharmony_ci q->lif->netdev->name, q->name); 71562306a36Sopenharmony_ci stats->dma_map_err++; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci return dma_addr; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic int ionic_tx_map_skb(struct ionic_queue *q, struct sk_buff *skb, 72162306a36Sopenharmony_ci struct ionic_desc_info *desc_info) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct ionic_buf_info *buf_info = desc_info->bufs; 72462306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 72562306a36Sopenharmony_ci struct device *dev = q->dev; 72662306a36Sopenharmony_ci dma_addr_t dma_addr; 72762306a36Sopenharmony_ci unsigned int nfrags; 72862306a36Sopenharmony_ci skb_frag_t *frag; 72962306a36Sopenharmony_ci int frag_idx; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci dma_addr = ionic_tx_map_single(q, skb->data, skb_headlen(skb)); 73262306a36Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) { 73362306a36Sopenharmony_ci stats->dma_map_err++; 73462306a36Sopenharmony_ci return -EIO; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci buf_info->dma_addr = dma_addr; 73762306a36Sopenharmony_ci buf_info->len = skb_headlen(skb); 73862306a36Sopenharmony_ci buf_info++; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci frag = skb_shinfo(skb)->frags; 74162306a36Sopenharmony_ci nfrags = skb_shinfo(skb)->nr_frags; 74262306a36Sopenharmony_ci for (frag_idx = 0; frag_idx < nfrags; frag_idx++, frag++) { 74362306a36Sopenharmony_ci dma_addr = ionic_tx_map_frag(q, frag, 0, skb_frag_size(frag)); 74462306a36Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) { 74562306a36Sopenharmony_ci stats->dma_map_err++; 74662306a36Sopenharmony_ci goto dma_fail; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci buf_info->dma_addr = dma_addr; 74962306a36Sopenharmony_ci buf_info->len = skb_frag_size(frag); 75062306a36Sopenharmony_ci buf_info++; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci desc_info->nbufs = 1 + nfrags; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return 0; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cidma_fail: 75862306a36Sopenharmony_ci /* unwind the frag mappings and the head mapping */ 75962306a36Sopenharmony_ci while (frag_idx > 0) { 76062306a36Sopenharmony_ci frag_idx--; 76162306a36Sopenharmony_ci buf_info--; 76262306a36Sopenharmony_ci dma_unmap_page(dev, buf_info->dma_addr, 76362306a36Sopenharmony_ci buf_info->len, DMA_TO_DEVICE); 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci dma_unmap_single(dev, buf_info->dma_addr, buf_info->len, DMA_TO_DEVICE); 76662306a36Sopenharmony_ci return -EIO; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic void ionic_tx_desc_unmap_bufs(struct ionic_queue *q, 77062306a36Sopenharmony_ci struct ionic_desc_info *desc_info) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct ionic_buf_info *buf_info = desc_info->bufs; 77362306a36Sopenharmony_ci struct device *dev = q->dev; 77462306a36Sopenharmony_ci unsigned int i; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (!desc_info->nbufs) 77762306a36Sopenharmony_ci return; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr, 78062306a36Sopenharmony_ci buf_info->len, DMA_TO_DEVICE); 78162306a36Sopenharmony_ci buf_info++; 78262306a36Sopenharmony_ci for (i = 1; i < desc_info->nbufs; i++, buf_info++) 78362306a36Sopenharmony_ci dma_unmap_page(dev, (dma_addr_t)buf_info->dma_addr, 78462306a36Sopenharmony_ci buf_info->len, DMA_TO_DEVICE); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci desc_info->nbufs = 0; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic void ionic_tx_clean(struct ionic_queue *q, 79062306a36Sopenharmony_ci struct ionic_desc_info *desc_info, 79162306a36Sopenharmony_ci struct ionic_cq_info *cq_info, 79262306a36Sopenharmony_ci void *cb_arg) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 79562306a36Sopenharmony_ci struct ionic_qcq *qcq = q_to_qcq(q); 79662306a36Sopenharmony_ci struct sk_buff *skb = cb_arg; 79762306a36Sopenharmony_ci u16 qi; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ionic_tx_desc_unmap_bufs(q, desc_info); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (!skb) 80262306a36Sopenharmony_ci return; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci qi = skb_get_queue_mapping(skb); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (unlikely(q->features & IONIC_TXQ_F_HWSTAMP)) { 80762306a36Sopenharmony_ci if (cq_info) { 80862306a36Sopenharmony_ci struct skb_shared_hwtstamps hwts = {}; 80962306a36Sopenharmony_ci __le64 *cq_desc_hwstamp; 81062306a36Sopenharmony_ci u64 hwstamp; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci cq_desc_hwstamp = 81362306a36Sopenharmony_ci cq_info->cq_desc + 81462306a36Sopenharmony_ci qcq->cq.desc_size - 81562306a36Sopenharmony_ci sizeof(struct ionic_txq_comp) - 81662306a36Sopenharmony_ci IONIC_HWSTAMP_CQ_NEGOFFSET; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci hwstamp = le64_to_cpu(*cq_desc_hwstamp); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (hwstamp != IONIC_HWSTAMP_INVALID) { 82162306a36Sopenharmony_ci hwts.hwtstamp = ionic_lif_phc_ktime(q->lif, hwstamp); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 82462306a36Sopenharmony_ci skb_tstamp_tx(skb, &hwts); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci stats->hwstamp_valid++; 82762306a36Sopenharmony_ci } else { 82862306a36Sopenharmony_ci stats->hwstamp_invalid++; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci } else if (unlikely(__netif_subqueue_stopped(q->lif->netdev, qi))) { 83362306a36Sopenharmony_ci netif_wake_subqueue(q->lif->netdev, qi); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci desc_info->bytes = skb->len; 83762306a36Sopenharmony_ci stats->clean++; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci dev_consume_skb_any(skb); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cibool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct ionic_queue *q = cq->bound_q; 84562306a36Sopenharmony_ci struct ionic_desc_info *desc_info; 84662306a36Sopenharmony_ci struct ionic_txq_comp *comp; 84762306a36Sopenharmony_ci int bytes = 0; 84862306a36Sopenharmony_ci int pkts = 0; 84962306a36Sopenharmony_ci u16 index; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci comp = cq_info->cq_desc + cq->desc_size - sizeof(*comp); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (!color_match(comp->color, cq->done_color)) 85462306a36Sopenharmony_ci return false; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* clean the related q entries, there could be 85762306a36Sopenharmony_ci * several q entries completed for each cq completion 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci do { 86062306a36Sopenharmony_ci desc_info = &q->info[q->tail_idx]; 86162306a36Sopenharmony_ci desc_info->bytes = 0; 86262306a36Sopenharmony_ci index = q->tail_idx; 86362306a36Sopenharmony_ci q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); 86462306a36Sopenharmony_ci ionic_tx_clean(q, desc_info, cq_info, desc_info->cb_arg); 86562306a36Sopenharmony_ci if (desc_info->cb_arg) { 86662306a36Sopenharmony_ci pkts++; 86762306a36Sopenharmony_ci bytes += desc_info->bytes; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci desc_info->cb = NULL; 87062306a36Sopenharmony_ci desc_info->cb_arg = NULL; 87162306a36Sopenharmony_ci } while (index != le16_to_cpu(comp->comp_index)); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (pkts && bytes && !unlikely(q->features & IONIC_TXQ_F_HWSTAMP)) 87462306a36Sopenharmony_ci netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return true; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_civoid ionic_tx_flush(struct ionic_cq *cq) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct ionic_dev *idev = &cq->lif->ionic->idev; 88262306a36Sopenharmony_ci u32 work_done; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci work_done = ionic_cq_service(cq, cq->num_descs, 88562306a36Sopenharmony_ci ionic_tx_service, NULL, NULL); 88662306a36Sopenharmony_ci if (work_done) 88762306a36Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index, 88862306a36Sopenharmony_ci work_done, IONIC_INTR_CRED_RESET_COALESCE); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_civoid ionic_tx_empty(struct ionic_queue *q) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct ionic_desc_info *desc_info; 89462306a36Sopenharmony_ci int bytes = 0; 89562306a36Sopenharmony_ci int pkts = 0; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* walk the not completed tx entries, if any */ 89862306a36Sopenharmony_ci while (q->head_idx != q->tail_idx) { 89962306a36Sopenharmony_ci desc_info = &q->info[q->tail_idx]; 90062306a36Sopenharmony_ci desc_info->bytes = 0; 90162306a36Sopenharmony_ci q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); 90262306a36Sopenharmony_ci ionic_tx_clean(q, desc_info, NULL, desc_info->cb_arg); 90362306a36Sopenharmony_ci if (desc_info->cb_arg) { 90462306a36Sopenharmony_ci pkts++; 90562306a36Sopenharmony_ci bytes += desc_info->bytes; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci desc_info->cb = NULL; 90862306a36Sopenharmony_ci desc_info->cb_arg = NULL; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (pkts && bytes && !unlikely(q->features & IONIC_TXQ_F_HWSTAMP)) 91262306a36Sopenharmony_ci netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes); 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci int err; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci err = skb_cow_head(skb, 0); 92062306a36Sopenharmony_ci if (err) 92162306a36Sopenharmony_ci return err; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_IP)) { 92462306a36Sopenharmony_ci inner_ip_hdr(skb)->check = 0; 92562306a36Sopenharmony_ci inner_tcp_hdr(skb)->check = 92662306a36Sopenharmony_ci ~csum_tcpudp_magic(inner_ip_hdr(skb)->saddr, 92762306a36Sopenharmony_ci inner_ip_hdr(skb)->daddr, 92862306a36Sopenharmony_ci 0, IPPROTO_TCP, 0); 92962306a36Sopenharmony_ci } else if (skb->protocol == cpu_to_be16(ETH_P_IPV6)) { 93062306a36Sopenharmony_ci inner_tcp_hdr(skb)->check = 93162306a36Sopenharmony_ci ~csum_ipv6_magic(&inner_ipv6_hdr(skb)->saddr, 93262306a36Sopenharmony_ci &inner_ipv6_hdr(skb)->daddr, 93362306a36Sopenharmony_ci 0, IPPROTO_TCP, 0); 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return 0; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic int ionic_tx_tcp_pseudo_csum(struct sk_buff *skb) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci int err; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci err = skb_cow_head(skb, 0); 94462306a36Sopenharmony_ci if (err) 94562306a36Sopenharmony_ci return err; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_IP)) { 94862306a36Sopenharmony_ci ip_hdr(skb)->check = 0; 94962306a36Sopenharmony_ci tcp_hdr(skb)->check = 95062306a36Sopenharmony_ci ~csum_tcpudp_magic(ip_hdr(skb)->saddr, 95162306a36Sopenharmony_ci ip_hdr(skb)->daddr, 95262306a36Sopenharmony_ci 0, IPPROTO_TCP, 0); 95362306a36Sopenharmony_ci } else if (skb->protocol == cpu_to_be16(ETH_P_IPV6)) { 95462306a36Sopenharmony_ci tcp_v6_gso_csum_prep(skb); 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic void ionic_tx_tso_post(struct ionic_queue *q, 96162306a36Sopenharmony_ci struct ionic_desc_info *desc_info, 96262306a36Sopenharmony_ci struct sk_buff *skb, 96362306a36Sopenharmony_ci dma_addr_t addr, u8 nsge, u16 len, 96462306a36Sopenharmony_ci unsigned int hdrlen, unsigned int mss, 96562306a36Sopenharmony_ci bool outer_csum, 96662306a36Sopenharmony_ci u16 vlan_tci, bool has_vlan, 96762306a36Sopenharmony_ci bool start, bool done) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci struct ionic_txq_desc *desc = desc_info->desc; 97062306a36Sopenharmony_ci u8 flags = 0; 97162306a36Sopenharmony_ci u64 cmd; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0; 97462306a36Sopenharmony_ci flags |= outer_csum ? IONIC_TXQ_DESC_FLAG_ENCAP : 0; 97562306a36Sopenharmony_ci flags |= start ? IONIC_TXQ_DESC_FLAG_TSO_SOT : 0; 97662306a36Sopenharmony_ci flags |= done ? IONIC_TXQ_DESC_FLAG_TSO_EOT : 0; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_TSO, flags, nsge, addr); 97962306a36Sopenharmony_ci desc->cmd = cpu_to_le64(cmd); 98062306a36Sopenharmony_ci desc->len = cpu_to_le16(len); 98162306a36Sopenharmony_ci desc->vlan_tci = cpu_to_le16(vlan_tci); 98262306a36Sopenharmony_ci desc->hdr_len = cpu_to_le16(hdrlen); 98362306a36Sopenharmony_ci desc->mss = cpu_to_le16(mss); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci ionic_write_cmb_desc(q, desc_info->cmb_desc, desc); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (start) { 98862306a36Sopenharmony_ci skb_tx_timestamp(skb); 98962306a36Sopenharmony_ci if (!unlikely(q->features & IONIC_TXQ_F_HWSTAMP)) 99062306a36Sopenharmony_ci netdev_tx_sent_queue(q_to_ndq(q), skb->len); 99162306a36Sopenharmony_ci ionic_txq_post(q, false, ionic_tx_clean, skb); 99262306a36Sopenharmony_ci } else { 99362306a36Sopenharmony_ci ionic_txq_post(q, done, NULL, NULL); 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 100062306a36Sopenharmony_ci struct ionic_desc_info *desc_info; 100162306a36Sopenharmony_ci struct ionic_buf_info *buf_info; 100262306a36Sopenharmony_ci struct ionic_txq_sg_elem *elem; 100362306a36Sopenharmony_ci struct ionic_txq_desc *desc; 100462306a36Sopenharmony_ci unsigned int chunk_len; 100562306a36Sopenharmony_ci unsigned int frag_rem; 100662306a36Sopenharmony_ci unsigned int tso_rem; 100762306a36Sopenharmony_ci unsigned int seg_rem; 100862306a36Sopenharmony_ci dma_addr_t desc_addr; 100962306a36Sopenharmony_ci dma_addr_t frag_addr; 101062306a36Sopenharmony_ci unsigned int hdrlen; 101162306a36Sopenharmony_ci unsigned int len; 101262306a36Sopenharmony_ci unsigned int mss; 101362306a36Sopenharmony_ci bool start, done; 101462306a36Sopenharmony_ci bool outer_csum; 101562306a36Sopenharmony_ci bool has_vlan; 101662306a36Sopenharmony_ci u16 desc_len; 101762306a36Sopenharmony_ci u8 desc_nsge; 101862306a36Sopenharmony_ci u16 vlan_tci; 101962306a36Sopenharmony_ci bool encap; 102062306a36Sopenharmony_ci int err; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci desc_info = &q->info[q->head_idx]; 102362306a36Sopenharmony_ci buf_info = desc_info->bufs; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (unlikely(ionic_tx_map_skb(q, skb, desc_info))) 102662306a36Sopenharmony_ci return -EIO; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci len = skb->len; 102962306a36Sopenharmony_ci mss = skb_shinfo(skb)->gso_size; 103062306a36Sopenharmony_ci outer_csum = (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE | 103162306a36Sopenharmony_ci SKB_GSO_GRE_CSUM | 103262306a36Sopenharmony_ci SKB_GSO_IPXIP4 | 103362306a36Sopenharmony_ci SKB_GSO_IPXIP6 | 103462306a36Sopenharmony_ci SKB_GSO_UDP_TUNNEL | 103562306a36Sopenharmony_ci SKB_GSO_UDP_TUNNEL_CSUM)); 103662306a36Sopenharmony_ci has_vlan = !!skb_vlan_tag_present(skb); 103762306a36Sopenharmony_ci vlan_tci = skb_vlan_tag_get(skb); 103862306a36Sopenharmony_ci encap = skb->encapsulation; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* Preload inner-most TCP csum field with IP pseudo hdr 104162306a36Sopenharmony_ci * calculated with IP length set to zero. HW will later 104262306a36Sopenharmony_ci * add in length to each TCP segment resulting from the TSO. 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if (encap) 104662306a36Sopenharmony_ci err = ionic_tx_tcp_inner_pseudo_csum(skb); 104762306a36Sopenharmony_ci else 104862306a36Sopenharmony_ci err = ionic_tx_tcp_pseudo_csum(skb); 104962306a36Sopenharmony_ci if (err) { 105062306a36Sopenharmony_ci /* clean up mapping from ionic_tx_map_skb */ 105162306a36Sopenharmony_ci ionic_tx_desc_unmap_bufs(q, desc_info); 105262306a36Sopenharmony_ci return err; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci if (encap) 105662306a36Sopenharmony_ci hdrlen = skb_inner_tcp_all_headers(skb); 105762306a36Sopenharmony_ci else 105862306a36Sopenharmony_ci hdrlen = skb_tcp_all_headers(skb); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci tso_rem = len; 106162306a36Sopenharmony_ci seg_rem = min(tso_rem, hdrlen + mss); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci frag_addr = 0; 106462306a36Sopenharmony_ci frag_rem = 0; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci start = true; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci while (tso_rem > 0) { 106962306a36Sopenharmony_ci desc = NULL; 107062306a36Sopenharmony_ci elem = NULL; 107162306a36Sopenharmony_ci desc_addr = 0; 107262306a36Sopenharmony_ci desc_len = 0; 107362306a36Sopenharmony_ci desc_nsge = 0; 107462306a36Sopenharmony_ci /* use fragments until we have enough to post a single descriptor */ 107562306a36Sopenharmony_ci while (seg_rem > 0) { 107662306a36Sopenharmony_ci /* if the fragment is exhausted then move to the next one */ 107762306a36Sopenharmony_ci if (frag_rem == 0) { 107862306a36Sopenharmony_ci /* grab the next fragment */ 107962306a36Sopenharmony_ci frag_addr = buf_info->dma_addr; 108062306a36Sopenharmony_ci frag_rem = buf_info->len; 108162306a36Sopenharmony_ci buf_info++; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci chunk_len = min(frag_rem, seg_rem); 108462306a36Sopenharmony_ci if (!desc) { 108562306a36Sopenharmony_ci /* fill main descriptor */ 108662306a36Sopenharmony_ci desc = desc_info->txq_desc; 108762306a36Sopenharmony_ci elem = desc_info->txq_sg_desc->elems; 108862306a36Sopenharmony_ci desc_addr = frag_addr; 108962306a36Sopenharmony_ci desc_len = chunk_len; 109062306a36Sopenharmony_ci } else { 109162306a36Sopenharmony_ci /* fill sg descriptor */ 109262306a36Sopenharmony_ci elem->addr = cpu_to_le64(frag_addr); 109362306a36Sopenharmony_ci elem->len = cpu_to_le16(chunk_len); 109462306a36Sopenharmony_ci elem++; 109562306a36Sopenharmony_ci desc_nsge++; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci frag_addr += chunk_len; 109862306a36Sopenharmony_ci frag_rem -= chunk_len; 109962306a36Sopenharmony_ci tso_rem -= chunk_len; 110062306a36Sopenharmony_ci seg_rem -= chunk_len; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci seg_rem = min(tso_rem, mss); 110362306a36Sopenharmony_ci done = (tso_rem == 0); 110462306a36Sopenharmony_ci /* post descriptor */ 110562306a36Sopenharmony_ci ionic_tx_tso_post(q, desc_info, skb, 110662306a36Sopenharmony_ci desc_addr, desc_nsge, desc_len, 110762306a36Sopenharmony_ci hdrlen, mss, outer_csum, vlan_tci, has_vlan, 110862306a36Sopenharmony_ci start, done); 110962306a36Sopenharmony_ci start = false; 111062306a36Sopenharmony_ci /* Buffer information is stored with the first tso descriptor */ 111162306a36Sopenharmony_ci desc_info = &q->info[q->head_idx]; 111262306a36Sopenharmony_ci desc_info->nbufs = 0; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci stats->pkts += DIV_ROUND_UP(len - hdrlen, mss); 111662306a36Sopenharmony_ci stats->bytes += len; 111762306a36Sopenharmony_ci stats->tso++; 111862306a36Sopenharmony_ci stats->tso_bytes = len; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci return 0; 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic void ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, 112462306a36Sopenharmony_ci struct ionic_desc_info *desc_info) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci struct ionic_txq_desc *desc = desc_info->txq_desc; 112762306a36Sopenharmony_ci struct ionic_buf_info *buf_info = desc_info->bufs; 112862306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 112962306a36Sopenharmony_ci bool has_vlan; 113062306a36Sopenharmony_ci u8 flags = 0; 113162306a36Sopenharmony_ci bool encap; 113262306a36Sopenharmony_ci u64 cmd; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci has_vlan = !!skb_vlan_tag_present(skb); 113562306a36Sopenharmony_ci encap = skb->encapsulation; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0; 113862306a36Sopenharmony_ci flags |= encap ? IONIC_TXQ_DESC_FLAG_ENCAP : 0; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_PARTIAL, 114162306a36Sopenharmony_ci flags, skb_shinfo(skb)->nr_frags, 114262306a36Sopenharmony_ci buf_info->dma_addr); 114362306a36Sopenharmony_ci desc->cmd = cpu_to_le64(cmd); 114462306a36Sopenharmony_ci desc->len = cpu_to_le16(buf_info->len); 114562306a36Sopenharmony_ci if (has_vlan) { 114662306a36Sopenharmony_ci desc->vlan_tci = cpu_to_le16(skb_vlan_tag_get(skb)); 114762306a36Sopenharmony_ci stats->vlan_inserted++; 114862306a36Sopenharmony_ci } else { 114962306a36Sopenharmony_ci desc->vlan_tci = 0; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci desc->csum_start = cpu_to_le16(skb_checksum_start_offset(skb)); 115262306a36Sopenharmony_ci desc->csum_offset = cpu_to_le16(skb->csum_offset); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci ionic_write_cmb_desc(q, desc_info->cmb_desc, desc); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (skb_csum_is_sctp(skb)) 115762306a36Sopenharmony_ci stats->crc32_csum++; 115862306a36Sopenharmony_ci else 115962306a36Sopenharmony_ci stats->csum++; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic void ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, 116362306a36Sopenharmony_ci struct ionic_desc_info *desc_info) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct ionic_txq_desc *desc = desc_info->txq_desc; 116662306a36Sopenharmony_ci struct ionic_buf_info *buf_info = desc_info->bufs; 116762306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 116862306a36Sopenharmony_ci bool has_vlan; 116962306a36Sopenharmony_ci u8 flags = 0; 117062306a36Sopenharmony_ci bool encap; 117162306a36Sopenharmony_ci u64 cmd; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci has_vlan = !!skb_vlan_tag_present(skb); 117462306a36Sopenharmony_ci encap = skb->encapsulation; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0; 117762306a36Sopenharmony_ci flags |= encap ? IONIC_TXQ_DESC_FLAG_ENCAP : 0; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_NONE, 118062306a36Sopenharmony_ci flags, skb_shinfo(skb)->nr_frags, 118162306a36Sopenharmony_ci buf_info->dma_addr); 118262306a36Sopenharmony_ci desc->cmd = cpu_to_le64(cmd); 118362306a36Sopenharmony_ci desc->len = cpu_to_le16(buf_info->len); 118462306a36Sopenharmony_ci if (has_vlan) { 118562306a36Sopenharmony_ci desc->vlan_tci = cpu_to_le16(skb_vlan_tag_get(skb)); 118662306a36Sopenharmony_ci stats->vlan_inserted++; 118762306a36Sopenharmony_ci } else { 118862306a36Sopenharmony_ci desc->vlan_tci = 0; 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci desc->csum_start = 0; 119162306a36Sopenharmony_ci desc->csum_offset = 0; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci ionic_write_cmb_desc(q, desc_info->cmb_desc, desc); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci stats->csum_none++; 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, 119962306a36Sopenharmony_ci struct ionic_desc_info *desc_info) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct ionic_txq_sg_desc *sg_desc = desc_info->txq_sg_desc; 120262306a36Sopenharmony_ci struct ionic_buf_info *buf_info = &desc_info->bufs[1]; 120362306a36Sopenharmony_ci struct ionic_txq_sg_elem *elem = sg_desc->elems; 120462306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 120562306a36Sopenharmony_ci unsigned int i; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++, buf_info++, elem++) { 120862306a36Sopenharmony_ci elem->addr = cpu_to_le64(buf_info->dma_addr); 120962306a36Sopenharmony_ci elem->len = cpu_to_le16(buf_info->len); 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci stats->frags += skb_shinfo(skb)->nr_frags; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci struct ionic_desc_info *desc_info = &q->info[q->head_idx]; 121862306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (unlikely(ionic_tx_map_skb(q, skb, desc_info))) 122162306a36Sopenharmony_ci return -EIO; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci /* set up the initial descriptor */ 122462306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 122562306a36Sopenharmony_ci ionic_tx_calc_csum(q, skb, desc_info); 122662306a36Sopenharmony_ci else 122762306a36Sopenharmony_ci ionic_tx_calc_no_csum(q, skb, desc_info); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* add frags */ 123062306a36Sopenharmony_ci ionic_tx_skb_frags(q, skb, desc_info); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci skb_tx_timestamp(skb); 123362306a36Sopenharmony_ci stats->pkts++; 123462306a36Sopenharmony_ci stats->bytes += skb->len; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci if (!unlikely(q->features & IONIC_TXQ_F_HWSTAMP)) 123762306a36Sopenharmony_ci netdev_tx_sent_queue(q_to_ndq(q), skb->len); 123862306a36Sopenharmony_ci ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 124662306a36Sopenharmony_ci int ndescs; 124762306a36Sopenharmony_ci int err; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* Each desc is mss long max, so a descriptor for each gso_seg */ 125062306a36Sopenharmony_ci if (skb_is_gso(skb)) 125162306a36Sopenharmony_ci ndescs = skb_shinfo(skb)->gso_segs; 125262306a36Sopenharmony_ci else 125362306a36Sopenharmony_ci ndescs = 1; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci /* If non-TSO, just need 1 desc and nr_frags sg elems */ 125662306a36Sopenharmony_ci if (skb_shinfo(skb)->nr_frags <= q->max_sg_elems) 125762306a36Sopenharmony_ci return ndescs; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* Too many frags, so linearize */ 126062306a36Sopenharmony_ci err = skb_linearize(skb); 126162306a36Sopenharmony_ci if (err) 126262306a36Sopenharmony_ci return err; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci stats->linearize++; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci return ndescs; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci int stopped = 0; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (unlikely(!ionic_q_has_space(q, ndescs))) { 127462306a36Sopenharmony_ci netif_stop_subqueue(q->lif->netdev, q->index); 127562306a36Sopenharmony_ci stopped = 1; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci /* Might race with ionic_tx_clean, check again */ 127862306a36Sopenharmony_ci smp_rmb(); 127962306a36Sopenharmony_ci if (ionic_q_has_space(q, ndescs)) { 128062306a36Sopenharmony_ci netif_wake_subqueue(q->lif->netdev, q->index); 128162306a36Sopenharmony_ci stopped = 0; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci return stopped; 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb, 128962306a36Sopenharmony_ci struct net_device *netdev) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 129262306a36Sopenharmony_ci struct ionic_queue *q = &lif->hwstamp_txq->q; 129362306a36Sopenharmony_ci int err, ndescs; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci /* Does not stop/start txq, because we post to a separate tx queue 129662306a36Sopenharmony_ci * for timestamping, and if a packet can't be posted immediately to 129762306a36Sopenharmony_ci * the timestamping queue, it is dropped. 129862306a36Sopenharmony_ci */ 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci ndescs = ionic_tx_descs_needed(q, skb); 130162306a36Sopenharmony_ci if (unlikely(ndescs < 0)) 130262306a36Sopenharmony_ci goto err_out_drop; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (unlikely(!ionic_q_has_space(q, ndescs))) 130562306a36Sopenharmony_ci goto err_out_drop; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_HW_TSTAMP; 130862306a36Sopenharmony_ci if (skb_is_gso(skb)) 130962306a36Sopenharmony_ci err = ionic_tx_tso(q, skb); 131062306a36Sopenharmony_ci else 131162306a36Sopenharmony_ci err = ionic_tx(q, skb); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if (err) 131462306a36Sopenharmony_ci goto err_out_drop; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci return NETDEV_TX_OK; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cierr_out_drop: 131962306a36Sopenharmony_ci q->drop++; 132062306a36Sopenharmony_ci dev_kfree_skb(skb); 132162306a36Sopenharmony_ci return NETDEV_TX_OK; 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cinetdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci u16 queue_index = skb_get_queue_mapping(skb); 132762306a36Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 132862306a36Sopenharmony_ci struct ionic_queue *q; 132962306a36Sopenharmony_ci int ndescs; 133062306a36Sopenharmony_ci int err; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (unlikely(!test_bit(IONIC_LIF_F_UP, lif->state))) { 133362306a36Sopenharmony_ci dev_kfree_skb(skb); 133462306a36Sopenharmony_ci return NETDEV_TX_OK; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) 133862306a36Sopenharmony_ci if (lif->hwstamp_txq && lif->phc->ts_config_tx_mode) 133962306a36Sopenharmony_ci return ionic_start_hwstamp_xmit(skb, netdev); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci if (unlikely(queue_index >= lif->nxqs)) 134262306a36Sopenharmony_ci queue_index = 0; 134362306a36Sopenharmony_ci q = &lif->txqcqs[queue_index]->q; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci ndescs = ionic_tx_descs_needed(q, skb); 134662306a36Sopenharmony_ci if (ndescs < 0) 134762306a36Sopenharmony_ci goto err_out_drop; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (unlikely(ionic_maybe_stop_tx(q, ndescs))) 135062306a36Sopenharmony_ci return NETDEV_TX_BUSY; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (skb_is_gso(skb)) 135362306a36Sopenharmony_ci err = ionic_tx_tso(q, skb); 135462306a36Sopenharmony_ci else 135562306a36Sopenharmony_ci err = ionic_tx(q, skb); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci if (err) 135862306a36Sopenharmony_ci goto err_out_drop; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci /* Stop the queue if there aren't descriptors for the next packet. 136162306a36Sopenharmony_ci * Since our SG lists per descriptor take care of most of the possible 136262306a36Sopenharmony_ci * fragmentation, we don't need to have many descriptors available. 136362306a36Sopenharmony_ci */ 136462306a36Sopenharmony_ci ionic_maybe_stop_tx(q, 4); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci return NETDEV_TX_OK; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cierr_out_drop: 136962306a36Sopenharmony_ci q->drop++; 137062306a36Sopenharmony_ci dev_kfree_skb(skb); 137162306a36Sopenharmony_ci return NETDEV_TX_OK; 137262306a36Sopenharmony_ci} 1373