18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Atlantic Network Driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2014-2019 aQuantia Corporation 58c2ecf20Sopenharmony_ci * Copyright (C) 2019-2020 Marvell International Ltd. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* File aq_ring.c: Definition of functions for Rx/Tx rings. */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "aq_ring.h" 118c2ecf20Sopenharmony_ci#include "aq_nic.h" 128c2ecf20Sopenharmony_ci#include "aq_hw.h" 138c2ecf20Sopenharmony_ci#include "aq_hw_utils.h" 148c2ecf20Sopenharmony_ci#include "aq_ptp.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic inline void aq_free_rxpage(struct aq_rxpage *rxpage, struct device *dev) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci unsigned int len = PAGE_SIZE << rxpage->order; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci dma_unmap_page(dev, rxpage->daddr, len, DMA_FROM_DEVICE); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* Drop the ref for being in the ring. */ 268c2ecf20Sopenharmony_ci __free_pages(rxpage->page, rxpage->order); 278c2ecf20Sopenharmony_ci rxpage->page = NULL; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int aq_get_rxpage(struct aq_rxpage *rxpage, unsigned int order, 318c2ecf20Sopenharmony_ci struct device *dev) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct page *page; 348c2ecf20Sopenharmony_ci int ret = -ENOMEM; 358c2ecf20Sopenharmony_ci dma_addr_t daddr; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci page = dev_alloc_pages(order); 388c2ecf20Sopenharmony_ci if (unlikely(!page)) 398c2ecf20Sopenharmony_ci goto err_exit; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci daddr = dma_map_page(dev, page, 0, PAGE_SIZE << order, 428c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(dev, daddr))) 458c2ecf20Sopenharmony_ci goto free_page; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci rxpage->page = page; 488c2ecf20Sopenharmony_ci rxpage->daddr = daddr; 498c2ecf20Sopenharmony_ci rxpage->order = order; 508c2ecf20Sopenharmony_ci rxpage->pg_off = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cifree_page: 558c2ecf20Sopenharmony_ci __free_pages(page, order); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cierr_exit: 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int aq_get_rxpages(struct aq_ring_s *self, struct aq_ring_buff_s *rxbuf, 628c2ecf20Sopenharmony_ci int order) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci int ret; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (rxbuf->rxdata.page) { 678c2ecf20Sopenharmony_ci /* One means ring is the only user and can reuse */ 688c2ecf20Sopenharmony_ci if (page_ref_count(rxbuf->rxdata.page) > 1) { 698c2ecf20Sopenharmony_ci /* Try reuse buffer */ 708c2ecf20Sopenharmony_ci rxbuf->rxdata.pg_off += AQ_CFG_RX_FRAME_MAX; 718c2ecf20Sopenharmony_ci if (rxbuf->rxdata.pg_off + AQ_CFG_RX_FRAME_MAX <= 728c2ecf20Sopenharmony_ci (PAGE_SIZE << order)) { 738c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 748c2ecf20Sopenharmony_ci self->stats.rx.pg_flips++; 758c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 768c2ecf20Sopenharmony_ci } else { 778c2ecf20Sopenharmony_ci /* Buffer exhausted. We have other users and 788c2ecf20Sopenharmony_ci * should release this page and realloc 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci aq_free_rxpage(&rxbuf->rxdata, 818c2ecf20Sopenharmony_ci aq_nic_get_dev(self->aq_nic)); 828c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 838c2ecf20Sopenharmony_ci self->stats.rx.pg_losts++; 848c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci } else { 878c2ecf20Sopenharmony_ci rxbuf->rxdata.pg_off = 0; 888c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 898c2ecf20Sopenharmony_ci self->stats.rx.pg_reuses++; 908c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (!rxbuf->rxdata.page) { 958c2ecf20Sopenharmony_ci ret = aq_get_rxpage(&rxbuf->rxdata, order, 968c2ecf20Sopenharmony_ci aq_nic_get_dev(self->aq_nic)); 978c2ecf20Sopenharmony_ci if (ret) { 988c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 998c2ecf20Sopenharmony_ci self->stats.rx.alloc_fails++; 1008c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci return ret; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic struct aq_ring_s *aq_ring_alloc(struct aq_ring_s *self, 1098c2ecf20Sopenharmony_ci struct aq_nic_s *aq_nic) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int err = 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci self->buff_ring = 1148c2ecf20Sopenharmony_ci kcalloc(self->size, sizeof(struct aq_ring_buff_s), GFP_KERNEL); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!self->buff_ring) { 1178c2ecf20Sopenharmony_ci err = -ENOMEM; 1188c2ecf20Sopenharmony_ci goto err_exit; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci self->dx_ring = dma_alloc_coherent(aq_nic_get_dev(aq_nic), 1218c2ecf20Sopenharmony_ci self->size * self->dx_size, 1228c2ecf20Sopenharmony_ci &self->dx_ring_pa, GFP_KERNEL); 1238c2ecf20Sopenharmony_ci if (!self->dx_ring) { 1248c2ecf20Sopenharmony_ci err = -ENOMEM; 1258c2ecf20Sopenharmony_ci goto err_exit; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cierr_exit: 1298c2ecf20Sopenharmony_ci if (err < 0) { 1308c2ecf20Sopenharmony_ci aq_ring_free(self); 1318c2ecf20Sopenharmony_ci self = NULL; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return self; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistruct aq_ring_s *aq_ring_tx_alloc(struct aq_ring_s *self, 1388c2ecf20Sopenharmony_ci struct aq_nic_s *aq_nic, 1398c2ecf20Sopenharmony_ci unsigned int idx, 1408c2ecf20Sopenharmony_ci struct aq_nic_cfg_s *aq_nic_cfg) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int err = 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci self->aq_nic = aq_nic; 1458c2ecf20Sopenharmony_ci self->idx = idx; 1468c2ecf20Sopenharmony_ci self->size = aq_nic_cfg->txds; 1478c2ecf20Sopenharmony_ci self->dx_size = aq_nic_cfg->aq_hw_caps->txd_size; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci self = aq_ring_alloc(self, aq_nic); 1508c2ecf20Sopenharmony_ci if (!self) { 1518c2ecf20Sopenharmony_ci err = -ENOMEM; 1528c2ecf20Sopenharmony_ci goto err_exit; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cierr_exit: 1568c2ecf20Sopenharmony_ci if (err < 0) { 1578c2ecf20Sopenharmony_ci aq_ring_free(self); 1588c2ecf20Sopenharmony_ci self = NULL; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return self; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistruct aq_ring_s *aq_ring_rx_alloc(struct aq_ring_s *self, 1658c2ecf20Sopenharmony_ci struct aq_nic_s *aq_nic, 1668c2ecf20Sopenharmony_ci unsigned int idx, 1678c2ecf20Sopenharmony_ci struct aq_nic_cfg_s *aq_nic_cfg) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int err = 0; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci self->aq_nic = aq_nic; 1728c2ecf20Sopenharmony_ci self->idx = idx; 1738c2ecf20Sopenharmony_ci self->size = aq_nic_cfg->rxds; 1748c2ecf20Sopenharmony_ci self->dx_size = aq_nic_cfg->aq_hw_caps->rxd_size; 1758c2ecf20Sopenharmony_ci self->page_order = fls(AQ_CFG_RX_FRAME_MAX / PAGE_SIZE + 1768c2ecf20Sopenharmony_ci (AQ_CFG_RX_FRAME_MAX % PAGE_SIZE ? 1 : 0)) - 1; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (aq_nic_cfg->rxpageorder > self->page_order) 1798c2ecf20Sopenharmony_ci self->page_order = aq_nic_cfg->rxpageorder; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci self = aq_ring_alloc(self, aq_nic); 1828c2ecf20Sopenharmony_ci if (!self) { 1838c2ecf20Sopenharmony_ci err = -ENOMEM; 1848c2ecf20Sopenharmony_ci goto err_exit; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cierr_exit: 1888c2ecf20Sopenharmony_ci if (err < 0) { 1898c2ecf20Sopenharmony_ci aq_ring_free(self); 1908c2ecf20Sopenharmony_ci self = NULL; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return self; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct aq_ring_s * 1978c2ecf20Sopenharmony_ciaq_ring_hwts_rx_alloc(struct aq_ring_s *self, struct aq_nic_s *aq_nic, 1988c2ecf20Sopenharmony_ci unsigned int idx, unsigned int size, unsigned int dx_size) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct device *dev = aq_nic_get_dev(aq_nic); 2018c2ecf20Sopenharmony_ci size_t sz = size * dx_size + AQ_CFG_RXDS_DEF; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci memset(self, 0, sizeof(*self)); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci self->aq_nic = aq_nic; 2068c2ecf20Sopenharmony_ci self->idx = idx; 2078c2ecf20Sopenharmony_ci self->size = size; 2088c2ecf20Sopenharmony_ci self->dx_size = dx_size; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci self->dx_ring = dma_alloc_coherent(dev, sz, &self->dx_ring_pa, 2118c2ecf20Sopenharmony_ci GFP_KERNEL); 2128c2ecf20Sopenharmony_ci if (!self->dx_ring) { 2138c2ecf20Sopenharmony_ci aq_ring_free(self); 2148c2ecf20Sopenharmony_ci return NULL; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return self; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciint aq_ring_init(struct aq_ring_s *self, const enum atl_ring_type ring_type) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci self->hw_head = 0; 2238c2ecf20Sopenharmony_ci self->sw_head = 0; 2248c2ecf20Sopenharmony_ci self->sw_tail = 0; 2258c2ecf20Sopenharmony_ci self->ring_type = ring_type; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (self->ring_type == ATL_RING_RX) 2288c2ecf20Sopenharmony_ci u64_stats_init(&self->stats.rx.syncp); 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci u64_stats_init(&self->stats.tx.syncp); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic inline bool aq_ring_dx_in_range(unsigned int h, unsigned int i, 2368c2ecf20Sopenharmony_ci unsigned int t) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci return (h < t) ? ((h < i) && (i < t)) : ((h < i) || (i < t)); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_civoid aq_ring_update_queue_state(struct aq_ring_s *ring) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci if (aq_ring_avail_dx(ring) <= AQ_CFG_SKB_FRAGS_MAX) 2448c2ecf20Sopenharmony_ci aq_ring_queue_stop(ring); 2458c2ecf20Sopenharmony_ci else if (aq_ring_avail_dx(ring) > AQ_CFG_RESTART_DESC_THRES) 2468c2ecf20Sopenharmony_ci aq_ring_queue_wake(ring); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_civoid aq_ring_queue_wake(struct aq_ring_s *ring) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct net_device *ndev = aq_nic_get_ndev(ring->aq_nic); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (__netif_subqueue_stopped(ndev, 2548c2ecf20Sopenharmony_ci AQ_NIC_RING2QMAP(ring->aq_nic, 2558c2ecf20Sopenharmony_ci ring->idx))) { 2568c2ecf20Sopenharmony_ci netif_wake_subqueue(ndev, 2578c2ecf20Sopenharmony_ci AQ_NIC_RING2QMAP(ring->aq_nic, ring->idx)); 2588c2ecf20Sopenharmony_ci u64_stats_update_begin(&ring->stats.tx.syncp); 2598c2ecf20Sopenharmony_ci ring->stats.tx.queue_restarts++; 2608c2ecf20Sopenharmony_ci u64_stats_update_end(&ring->stats.tx.syncp); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_civoid aq_ring_queue_stop(struct aq_ring_s *ring) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct net_device *ndev = aq_nic_get_ndev(ring->aq_nic); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!__netif_subqueue_stopped(ndev, 2698c2ecf20Sopenharmony_ci AQ_NIC_RING2QMAP(ring->aq_nic, 2708c2ecf20Sopenharmony_ci ring->idx))) 2718c2ecf20Sopenharmony_ci netif_stop_subqueue(ndev, 2728c2ecf20Sopenharmony_ci AQ_NIC_RING2QMAP(ring->aq_nic, ring->idx)); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cibool aq_ring_tx_clean(struct aq_ring_s *self) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct device *dev = aq_nic_get_dev(self->aq_nic); 2788c2ecf20Sopenharmony_ci unsigned int budget; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci for (budget = AQ_CFG_TX_CLEAN_BUDGET; 2818c2ecf20Sopenharmony_ci budget && self->sw_head != self->hw_head; budget--) { 2828c2ecf20Sopenharmony_ci struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (likely(buff->is_mapped)) { 2858c2ecf20Sopenharmony_ci if (unlikely(buff->is_sop)) { 2868c2ecf20Sopenharmony_ci if (!buff->is_eop && 2878c2ecf20Sopenharmony_ci buff->eop_index != 0xffffU && 2888c2ecf20Sopenharmony_ci (!aq_ring_dx_in_range(self->sw_head, 2898c2ecf20Sopenharmony_ci buff->eop_index, 2908c2ecf20Sopenharmony_ci self->hw_head))) 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci dma_unmap_single(dev, buff->pa, buff->len, 2948c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2958c2ecf20Sopenharmony_ci } else { 2968c2ecf20Sopenharmony_ci dma_unmap_page(dev, buff->pa, buff->len, 2978c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (unlikely(buff->is_eop)) { 3028c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.tx.syncp); 3038c2ecf20Sopenharmony_ci ++self->stats.tx.packets; 3048c2ecf20Sopenharmony_ci self->stats.tx.bytes += buff->skb->len; 3058c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.tx.syncp); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci dev_kfree_skb_any(buff->skb); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci buff->pa = 0U; 3108c2ecf20Sopenharmony_ci buff->eop_index = 0xffffU; 3118c2ecf20Sopenharmony_ci self->sw_head = aq_ring_next_dx(self, self->sw_head); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return !!budget; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void aq_rx_checksum(struct aq_ring_s *self, 3188c2ecf20Sopenharmony_ci struct aq_ring_buff_s *buff, 3198c2ecf20Sopenharmony_ci struct sk_buff *skb) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci if (!(self->aq_nic->ndev->features & NETIF_F_RXCSUM)) 3228c2ecf20Sopenharmony_ci return; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (unlikely(buff->is_cso_err)) { 3258c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 3268c2ecf20Sopenharmony_ci ++self->stats.rx.errors; 3278c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 3288c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci if (buff->is_ip_cso) { 3328c2ecf20Sopenharmony_ci __skb_incr_checksum_unnecessary(skb); 3338c2ecf20Sopenharmony_ci } else { 3348c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (buff->is_udp_cso || buff->is_tcp_cso) 3388c2ecf20Sopenharmony_ci __skb_incr_checksum_unnecessary(skb); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci#define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) 3428c2ecf20Sopenharmony_ciint aq_ring_rx_clean(struct aq_ring_s *self, 3438c2ecf20Sopenharmony_ci struct napi_struct *napi, 3448c2ecf20Sopenharmony_ci int *work_done, 3458c2ecf20Sopenharmony_ci int budget) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct net_device *ndev = aq_nic_get_ndev(self->aq_nic); 3488c2ecf20Sopenharmony_ci int err = 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci for (; (self->sw_head != self->hw_head) && budget; 3518c2ecf20Sopenharmony_ci self->sw_head = aq_ring_next_dx(self, self->sw_head), 3528c2ecf20Sopenharmony_ci --budget, ++(*work_done)) { 3538c2ecf20Sopenharmony_ci struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; 3548c2ecf20Sopenharmony_ci bool is_ptp_ring = aq_ptp_ring(self->aq_nic, self); 3558c2ecf20Sopenharmony_ci struct aq_ring_buff_s *buff_ = NULL; 3568c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 3578c2ecf20Sopenharmony_ci unsigned int next_ = 0U; 3588c2ecf20Sopenharmony_ci unsigned int i = 0U; 3598c2ecf20Sopenharmony_ci u16 hdr_len; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (buff->is_cleaned) 3628c2ecf20Sopenharmony_ci continue; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (!buff->is_eop) { 3658c2ecf20Sopenharmony_ci unsigned int frag_cnt = 0U; 3668c2ecf20Sopenharmony_ci buff_ = buff; 3678c2ecf20Sopenharmony_ci do { 3688c2ecf20Sopenharmony_ci bool is_rsc_completed = true; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (buff_->next >= self->size) { 3718c2ecf20Sopenharmony_ci err = -EIO; 3728c2ecf20Sopenharmony_ci goto err_exit; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci frag_cnt++; 3768c2ecf20Sopenharmony_ci next_ = buff_->next, 3778c2ecf20Sopenharmony_ci buff_ = &self->buff_ring[next_]; 3788c2ecf20Sopenharmony_ci is_rsc_completed = 3798c2ecf20Sopenharmony_ci aq_ring_dx_in_range(self->sw_head, 3808c2ecf20Sopenharmony_ci next_, 3818c2ecf20Sopenharmony_ci self->hw_head); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (unlikely(!is_rsc_completed) || 3848c2ecf20Sopenharmony_ci frag_cnt > MAX_SKB_FRAGS) { 3858c2ecf20Sopenharmony_ci err = 0; 3868c2ecf20Sopenharmony_ci goto err_exit; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci buff->is_error |= buff_->is_error; 3908c2ecf20Sopenharmony_ci buff->is_cso_err |= buff_->is_cso_err; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci } while (!buff_->is_eop); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (buff->is_error || 3958c2ecf20Sopenharmony_ci (buff->is_lro && buff->is_cso_err)) { 3968c2ecf20Sopenharmony_ci buff_ = buff; 3978c2ecf20Sopenharmony_ci do { 3988c2ecf20Sopenharmony_ci if (buff_->next >= self->size) { 3998c2ecf20Sopenharmony_ci err = -EIO; 4008c2ecf20Sopenharmony_ci goto err_exit; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci next_ = buff_->next, 4038c2ecf20Sopenharmony_ci buff_ = &self->buff_ring[next_]; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci buff_->is_cleaned = true; 4068c2ecf20Sopenharmony_ci } while (!buff_->is_eop); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 4098c2ecf20Sopenharmony_ci ++self->stats.rx.errors; 4108c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 4118c2ecf20Sopenharmony_ci continue; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (buff->is_error) { 4168c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 4178c2ecf20Sopenharmony_ci ++self->stats.rx.errors; 4188c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 4198c2ecf20Sopenharmony_ci continue; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci dma_sync_single_range_for_cpu(aq_nic_get_dev(self->aq_nic), 4238c2ecf20Sopenharmony_ci buff->rxdata.daddr, 4248c2ecf20Sopenharmony_ci buff->rxdata.pg_off, 4258c2ecf20Sopenharmony_ci buff->len, DMA_FROM_DEVICE); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci skb = napi_alloc_skb(napi, AQ_CFG_RX_HDR_SIZE); 4288c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 4298c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 4308c2ecf20Sopenharmony_ci self->stats.rx.skb_alloc_fails++; 4318c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 4328c2ecf20Sopenharmony_ci err = -ENOMEM; 4338c2ecf20Sopenharmony_ci goto err_exit; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci if (is_ptp_ring) 4368c2ecf20Sopenharmony_ci buff->len -= 4378c2ecf20Sopenharmony_ci aq_ptp_extract_ts(self->aq_nic, skb, 4388c2ecf20Sopenharmony_ci aq_buf_vaddr(&buff->rxdata), 4398c2ecf20Sopenharmony_ci buff->len); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci hdr_len = buff->len; 4428c2ecf20Sopenharmony_ci if (hdr_len > AQ_CFG_RX_HDR_SIZE) 4438c2ecf20Sopenharmony_ci hdr_len = eth_get_headlen(skb->dev, 4448c2ecf20Sopenharmony_ci aq_buf_vaddr(&buff->rxdata), 4458c2ecf20Sopenharmony_ci AQ_CFG_RX_HDR_SIZE); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci memcpy(__skb_put(skb, hdr_len), aq_buf_vaddr(&buff->rxdata), 4488c2ecf20Sopenharmony_ci ALIGN(hdr_len, sizeof(long))); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (buff->len - hdr_len > 0) { 4518c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, i++, buff->rxdata.page, 4528c2ecf20Sopenharmony_ci buff->rxdata.pg_off + hdr_len, 4538c2ecf20Sopenharmony_ci buff->len - hdr_len, 4548c2ecf20Sopenharmony_ci AQ_CFG_RX_FRAME_MAX); 4558c2ecf20Sopenharmony_ci page_ref_inc(buff->rxdata.page); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!buff->is_eop) { 4598c2ecf20Sopenharmony_ci buff_ = buff; 4608c2ecf20Sopenharmony_ci do { 4618c2ecf20Sopenharmony_ci next_ = buff_->next; 4628c2ecf20Sopenharmony_ci buff_ = &self->buff_ring[next_]; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci dma_sync_single_range_for_cpu(aq_nic_get_dev(self->aq_nic), 4658c2ecf20Sopenharmony_ci buff_->rxdata.daddr, 4668c2ecf20Sopenharmony_ci buff_->rxdata.pg_off, 4678c2ecf20Sopenharmony_ci buff_->len, 4688c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4698c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, i++, 4708c2ecf20Sopenharmony_ci buff_->rxdata.page, 4718c2ecf20Sopenharmony_ci buff_->rxdata.pg_off, 4728c2ecf20Sopenharmony_ci buff_->len, 4738c2ecf20Sopenharmony_ci AQ_CFG_RX_FRAME_MAX); 4748c2ecf20Sopenharmony_ci page_ref_inc(buff_->rxdata.page); 4758c2ecf20Sopenharmony_ci buff_->is_cleaned = 1; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci buff->is_ip_cso &= buff_->is_ip_cso; 4788c2ecf20Sopenharmony_ci buff->is_udp_cso &= buff_->is_udp_cso; 4798c2ecf20Sopenharmony_ci buff->is_tcp_cso &= buff_->is_tcp_cso; 4808c2ecf20Sopenharmony_ci buff->is_cso_err |= buff_->is_cso_err; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci } while (!buff_->is_eop); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (buff->is_vlan) 4868c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), 4878c2ecf20Sopenharmony_ci buff->vlan_rx_tag); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci aq_rx_checksum(self, buff, skb); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci skb_set_hash(skb, buff->rss_hash, 4948c2ecf20Sopenharmony_ci buff->is_hash_l4 ? PKT_HASH_TYPE_L4 : 4958c2ecf20Sopenharmony_ci PKT_HASH_TYPE_NONE); 4968c2ecf20Sopenharmony_ci /* Send all PTP traffic to 0 queue */ 4978c2ecf20Sopenharmony_ci skb_record_rx_queue(skb, 4988c2ecf20Sopenharmony_ci is_ptp_ring ? 0 4998c2ecf20Sopenharmony_ci : AQ_NIC_RING2QMAP(self->aq_nic, 5008c2ecf20Sopenharmony_ci self->idx)); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci u64_stats_update_begin(&self->stats.rx.syncp); 5038c2ecf20Sopenharmony_ci ++self->stats.rx.packets; 5048c2ecf20Sopenharmony_ci self->stats.rx.bytes += skb->len; 5058c2ecf20Sopenharmony_ci u64_stats_update_end(&self->stats.rx.syncp); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci napi_gro_receive(napi, skb); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cierr_exit: 5118c2ecf20Sopenharmony_ci return err; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_civoid aq_ring_hwts_rx_clean(struct aq_ring_s *self, struct aq_nic_s *aq_nic) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK) 5178c2ecf20Sopenharmony_ci while (self->sw_head != self->hw_head) { 5188c2ecf20Sopenharmony_ci u64 ns; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci aq_nic->aq_hw_ops->extract_hwts(aq_nic->aq_hw, 5218c2ecf20Sopenharmony_ci self->dx_ring + 5228c2ecf20Sopenharmony_ci (self->sw_head * self->dx_size), 5238c2ecf20Sopenharmony_ci self->dx_size, &ns); 5248c2ecf20Sopenharmony_ci aq_ptp_tx_hwtstamp(aq_nic, ns); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci self->sw_head = aq_ring_next_dx(self, self->sw_head); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci#endif 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ciint aq_ring_rx_fill(struct aq_ring_s *self) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci unsigned int page_order = self->page_order; 5348c2ecf20Sopenharmony_ci struct aq_ring_buff_s *buff = NULL; 5358c2ecf20Sopenharmony_ci int err = 0; 5368c2ecf20Sopenharmony_ci int i = 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (aq_ring_avail_dx(self) < min_t(unsigned int, AQ_CFG_RX_REFILL_THRES, 5398c2ecf20Sopenharmony_ci self->size / 2)) 5408c2ecf20Sopenharmony_ci return err; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci for (i = aq_ring_avail_dx(self); i--; 5438c2ecf20Sopenharmony_ci self->sw_tail = aq_ring_next_dx(self, self->sw_tail)) { 5448c2ecf20Sopenharmony_ci buff = &self->buff_ring[self->sw_tail]; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci buff->flags = 0U; 5478c2ecf20Sopenharmony_ci buff->len = AQ_CFG_RX_FRAME_MAX; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci err = aq_get_rxpages(self, buff, page_order); 5508c2ecf20Sopenharmony_ci if (err) 5518c2ecf20Sopenharmony_ci goto err_exit; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci buff->pa = aq_buf_daddr(&buff->rxdata); 5548c2ecf20Sopenharmony_ci buff = NULL; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cierr_exit: 5588c2ecf20Sopenharmony_ci return err; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_civoid aq_ring_rx_deinit(struct aq_ring_s *self) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci if (!self) 5648c2ecf20Sopenharmony_ci return; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci for (; self->sw_head != self->sw_tail; 5678c2ecf20Sopenharmony_ci self->sw_head = aq_ring_next_dx(self, self->sw_head)) { 5688c2ecf20Sopenharmony_ci struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci aq_free_rxpage(&buff->rxdata, aq_nic_get_dev(self->aq_nic)); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_civoid aq_ring_free(struct aq_ring_s *self) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci if (!self) 5778c2ecf20Sopenharmony_ci return; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci kfree(self->buff_ring); 5808c2ecf20Sopenharmony_ci self->buff_ring = NULL; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (self->dx_ring) { 5838c2ecf20Sopenharmony_ci dma_free_coherent(aq_nic_get_dev(self->aq_nic), 5848c2ecf20Sopenharmony_ci self->size * self->dx_size, self->dx_ring, 5858c2ecf20Sopenharmony_ci self->dx_ring_pa); 5868c2ecf20Sopenharmony_ci self->dx_ring = NULL; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ciunsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci unsigned int count; 5938c2ecf20Sopenharmony_ci unsigned int start; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (self->ring_type == ATL_RING_RX) { 5968c2ecf20Sopenharmony_ci /* This data should mimic aq_ethtool_queue_rx_stat_names structure */ 5978c2ecf20Sopenharmony_ci do { 5988c2ecf20Sopenharmony_ci count = 0; 5998c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&self->stats.rx.syncp); 6008c2ecf20Sopenharmony_ci data[count] = self->stats.rx.packets; 6018c2ecf20Sopenharmony_ci data[++count] = self->stats.rx.jumbo_packets; 6028c2ecf20Sopenharmony_ci data[++count] = self->stats.rx.lro_packets; 6038c2ecf20Sopenharmony_ci data[++count] = self->stats.rx.errors; 6048c2ecf20Sopenharmony_ci data[++count] = self->stats.rx.alloc_fails; 6058c2ecf20Sopenharmony_ci data[++count] = self->stats.rx.skb_alloc_fails; 6068c2ecf20Sopenharmony_ci data[++count] = self->stats.rx.polls; 6078c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&self->stats.rx.syncp, start)); 6088c2ecf20Sopenharmony_ci } else { 6098c2ecf20Sopenharmony_ci /* This data should mimic aq_ethtool_queue_tx_stat_names structure */ 6108c2ecf20Sopenharmony_ci do { 6118c2ecf20Sopenharmony_ci count = 0; 6128c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&self->stats.tx.syncp); 6138c2ecf20Sopenharmony_ci data[count] = self->stats.tx.packets; 6148c2ecf20Sopenharmony_ci data[++count] = self->stats.tx.queue_restarts; 6158c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&self->stats.tx.syncp, start)); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return ++count; 6198c2ecf20Sopenharmony_ci} 620