162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/etherdevice.h> 562306a36Sopenharmony_ci#include <net/ip6_checksum.h> 662306a36Sopenharmony_ci#include <net/page_pool/helpers.h> 762306a36Sopenharmony_ci#include <net/inet_ecn.h> 862306a36Sopenharmony_ci#include <linux/iopoll.h> 962306a36Sopenharmony_ci#include <linux/sctp.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <net/tcp.h> 1262306a36Sopenharmony_ci#include <net/ip.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "wx_type.h" 1562306a36Sopenharmony_ci#include "wx_lib.h" 1662306a36Sopenharmony_ci#include "wx_hw.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Lookup table mapping the HW PTYPE to the bit field for decoding */ 1962306a36Sopenharmony_cistatic struct wx_dec_ptype wx_ptype_lookup[256] = { 2062306a36Sopenharmony_ci /* L2: mac */ 2162306a36Sopenharmony_ci [0x11] = WX_PTT(L2, NONE, NONE, NONE, NONE, PAY2), 2262306a36Sopenharmony_ci [0x12] = WX_PTT(L2, NONE, NONE, NONE, TS, PAY2), 2362306a36Sopenharmony_ci [0x13] = WX_PTT(L2, NONE, NONE, NONE, NONE, PAY2), 2462306a36Sopenharmony_ci [0x14] = WX_PTT(L2, NONE, NONE, NONE, NONE, PAY2), 2562306a36Sopenharmony_ci [0x15] = WX_PTT(L2, NONE, NONE, NONE, NONE, NONE), 2662306a36Sopenharmony_ci [0x16] = WX_PTT(L2, NONE, NONE, NONE, NONE, PAY2), 2762306a36Sopenharmony_ci [0x17] = WX_PTT(L2, NONE, NONE, NONE, NONE, NONE), 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* L2: ethertype filter */ 3062306a36Sopenharmony_ci [0x18 ... 0x1F] = WX_PTT(L2, NONE, NONE, NONE, NONE, NONE), 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* L3: ip non-tunnel */ 3362306a36Sopenharmony_ci [0x21] = WX_PTT(IP, FGV4, NONE, NONE, NONE, PAY3), 3462306a36Sopenharmony_ci [0x22] = WX_PTT(IP, IPV4, NONE, NONE, NONE, PAY3), 3562306a36Sopenharmony_ci [0x23] = WX_PTT(IP, IPV4, NONE, NONE, UDP, PAY4), 3662306a36Sopenharmony_ci [0x24] = WX_PTT(IP, IPV4, NONE, NONE, TCP, PAY4), 3762306a36Sopenharmony_ci [0x25] = WX_PTT(IP, IPV4, NONE, NONE, SCTP, PAY4), 3862306a36Sopenharmony_ci [0x29] = WX_PTT(IP, FGV6, NONE, NONE, NONE, PAY3), 3962306a36Sopenharmony_ci [0x2A] = WX_PTT(IP, IPV6, NONE, NONE, NONE, PAY3), 4062306a36Sopenharmony_ci [0x2B] = WX_PTT(IP, IPV6, NONE, NONE, UDP, PAY3), 4162306a36Sopenharmony_ci [0x2C] = WX_PTT(IP, IPV6, NONE, NONE, TCP, PAY4), 4262306a36Sopenharmony_ci [0x2D] = WX_PTT(IP, IPV6, NONE, NONE, SCTP, PAY4), 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* L2: fcoe */ 4562306a36Sopenharmony_ci [0x30 ... 0x34] = WX_PTT(FCOE, NONE, NONE, NONE, NONE, PAY3), 4662306a36Sopenharmony_ci [0x38 ... 0x3C] = WX_PTT(FCOE, NONE, NONE, NONE, NONE, PAY3), 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* IPv4 --> IPv4/IPv6 */ 4962306a36Sopenharmony_ci [0x81] = WX_PTT(IP, IPV4, IPIP, FGV4, NONE, PAY3), 5062306a36Sopenharmony_ci [0x82] = WX_PTT(IP, IPV4, IPIP, IPV4, NONE, PAY3), 5162306a36Sopenharmony_ci [0x83] = WX_PTT(IP, IPV4, IPIP, IPV4, UDP, PAY4), 5262306a36Sopenharmony_ci [0x84] = WX_PTT(IP, IPV4, IPIP, IPV4, TCP, PAY4), 5362306a36Sopenharmony_ci [0x85] = WX_PTT(IP, IPV4, IPIP, IPV4, SCTP, PAY4), 5462306a36Sopenharmony_ci [0x89] = WX_PTT(IP, IPV4, IPIP, FGV6, NONE, PAY3), 5562306a36Sopenharmony_ci [0x8A] = WX_PTT(IP, IPV4, IPIP, IPV6, NONE, PAY3), 5662306a36Sopenharmony_ci [0x8B] = WX_PTT(IP, IPV4, IPIP, IPV6, UDP, PAY4), 5762306a36Sopenharmony_ci [0x8C] = WX_PTT(IP, IPV4, IPIP, IPV6, TCP, PAY4), 5862306a36Sopenharmony_ci [0x8D] = WX_PTT(IP, IPV4, IPIP, IPV6, SCTP, PAY4), 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* IPv4 --> GRE/NAT --> NONE/IPv4/IPv6 */ 6162306a36Sopenharmony_ci [0x90] = WX_PTT(IP, IPV4, IG, NONE, NONE, PAY3), 6262306a36Sopenharmony_ci [0x91] = WX_PTT(IP, IPV4, IG, FGV4, NONE, PAY3), 6362306a36Sopenharmony_ci [0x92] = WX_PTT(IP, IPV4, IG, IPV4, NONE, PAY3), 6462306a36Sopenharmony_ci [0x93] = WX_PTT(IP, IPV4, IG, IPV4, UDP, PAY4), 6562306a36Sopenharmony_ci [0x94] = WX_PTT(IP, IPV4, IG, IPV4, TCP, PAY4), 6662306a36Sopenharmony_ci [0x95] = WX_PTT(IP, IPV4, IG, IPV4, SCTP, PAY4), 6762306a36Sopenharmony_ci [0x99] = WX_PTT(IP, IPV4, IG, FGV6, NONE, PAY3), 6862306a36Sopenharmony_ci [0x9A] = WX_PTT(IP, IPV4, IG, IPV6, NONE, PAY3), 6962306a36Sopenharmony_ci [0x9B] = WX_PTT(IP, IPV4, IG, IPV6, UDP, PAY4), 7062306a36Sopenharmony_ci [0x9C] = WX_PTT(IP, IPV4, IG, IPV6, TCP, PAY4), 7162306a36Sopenharmony_ci [0x9D] = WX_PTT(IP, IPV4, IG, IPV6, SCTP, PAY4), 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* IPv4 --> GRE/NAT --> MAC --> NONE/IPv4/IPv6 */ 7462306a36Sopenharmony_ci [0xA0] = WX_PTT(IP, IPV4, IGM, NONE, NONE, PAY3), 7562306a36Sopenharmony_ci [0xA1] = WX_PTT(IP, IPV4, IGM, FGV4, NONE, PAY3), 7662306a36Sopenharmony_ci [0xA2] = WX_PTT(IP, IPV4, IGM, IPV4, NONE, PAY3), 7762306a36Sopenharmony_ci [0xA3] = WX_PTT(IP, IPV4, IGM, IPV4, UDP, PAY4), 7862306a36Sopenharmony_ci [0xA4] = WX_PTT(IP, IPV4, IGM, IPV4, TCP, PAY4), 7962306a36Sopenharmony_ci [0xA5] = WX_PTT(IP, IPV4, IGM, IPV4, SCTP, PAY4), 8062306a36Sopenharmony_ci [0xA9] = WX_PTT(IP, IPV4, IGM, FGV6, NONE, PAY3), 8162306a36Sopenharmony_ci [0xAA] = WX_PTT(IP, IPV4, IGM, IPV6, NONE, PAY3), 8262306a36Sopenharmony_ci [0xAB] = WX_PTT(IP, IPV4, IGM, IPV6, UDP, PAY4), 8362306a36Sopenharmony_ci [0xAC] = WX_PTT(IP, IPV4, IGM, IPV6, TCP, PAY4), 8462306a36Sopenharmony_ci [0xAD] = WX_PTT(IP, IPV4, IGM, IPV6, SCTP, PAY4), 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* IPv4 --> GRE/NAT --> MAC+VLAN --> NONE/IPv4/IPv6 */ 8762306a36Sopenharmony_ci [0xB0] = WX_PTT(IP, IPV4, IGMV, NONE, NONE, PAY3), 8862306a36Sopenharmony_ci [0xB1] = WX_PTT(IP, IPV4, IGMV, FGV4, NONE, PAY3), 8962306a36Sopenharmony_ci [0xB2] = WX_PTT(IP, IPV4, IGMV, IPV4, NONE, PAY3), 9062306a36Sopenharmony_ci [0xB3] = WX_PTT(IP, IPV4, IGMV, IPV4, UDP, PAY4), 9162306a36Sopenharmony_ci [0xB4] = WX_PTT(IP, IPV4, IGMV, IPV4, TCP, PAY4), 9262306a36Sopenharmony_ci [0xB5] = WX_PTT(IP, IPV4, IGMV, IPV4, SCTP, PAY4), 9362306a36Sopenharmony_ci [0xB9] = WX_PTT(IP, IPV4, IGMV, FGV6, NONE, PAY3), 9462306a36Sopenharmony_ci [0xBA] = WX_PTT(IP, IPV4, IGMV, IPV6, NONE, PAY3), 9562306a36Sopenharmony_ci [0xBB] = WX_PTT(IP, IPV4, IGMV, IPV6, UDP, PAY4), 9662306a36Sopenharmony_ci [0xBC] = WX_PTT(IP, IPV4, IGMV, IPV6, TCP, PAY4), 9762306a36Sopenharmony_ci [0xBD] = WX_PTT(IP, IPV4, IGMV, IPV6, SCTP, PAY4), 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* IPv6 --> IPv4/IPv6 */ 10062306a36Sopenharmony_ci [0xC1] = WX_PTT(IP, IPV6, IPIP, FGV4, NONE, PAY3), 10162306a36Sopenharmony_ci [0xC2] = WX_PTT(IP, IPV6, IPIP, IPV4, NONE, PAY3), 10262306a36Sopenharmony_ci [0xC3] = WX_PTT(IP, IPV6, IPIP, IPV4, UDP, PAY4), 10362306a36Sopenharmony_ci [0xC4] = WX_PTT(IP, IPV6, IPIP, IPV4, TCP, PAY4), 10462306a36Sopenharmony_ci [0xC5] = WX_PTT(IP, IPV6, IPIP, IPV4, SCTP, PAY4), 10562306a36Sopenharmony_ci [0xC9] = WX_PTT(IP, IPV6, IPIP, FGV6, NONE, PAY3), 10662306a36Sopenharmony_ci [0xCA] = WX_PTT(IP, IPV6, IPIP, IPV6, NONE, PAY3), 10762306a36Sopenharmony_ci [0xCB] = WX_PTT(IP, IPV6, IPIP, IPV6, UDP, PAY4), 10862306a36Sopenharmony_ci [0xCC] = WX_PTT(IP, IPV6, IPIP, IPV6, TCP, PAY4), 10962306a36Sopenharmony_ci [0xCD] = WX_PTT(IP, IPV6, IPIP, IPV6, SCTP, PAY4), 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* IPv6 --> GRE/NAT -> NONE/IPv4/IPv6 */ 11262306a36Sopenharmony_ci [0xD0] = WX_PTT(IP, IPV6, IG, NONE, NONE, PAY3), 11362306a36Sopenharmony_ci [0xD1] = WX_PTT(IP, IPV6, IG, FGV4, NONE, PAY3), 11462306a36Sopenharmony_ci [0xD2] = WX_PTT(IP, IPV6, IG, IPV4, NONE, PAY3), 11562306a36Sopenharmony_ci [0xD3] = WX_PTT(IP, IPV6, IG, IPV4, UDP, PAY4), 11662306a36Sopenharmony_ci [0xD4] = WX_PTT(IP, IPV6, IG, IPV4, TCP, PAY4), 11762306a36Sopenharmony_ci [0xD5] = WX_PTT(IP, IPV6, IG, IPV4, SCTP, PAY4), 11862306a36Sopenharmony_ci [0xD9] = WX_PTT(IP, IPV6, IG, FGV6, NONE, PAY3), 11962306a36Sopenharmony_ci [0xDA] = WX_PTT(IP, IPV6, IG, IPV6, NONE, PAY3), 12062306a36Sopenharmony_ci [0xDB] = WX_PTT(IP, IPV6, IG, IPV6, UDP, PAY4), 12162306a36Sopenharmony_ci [0xDC] = WX_PTT(IP, IPV6, IG, IPV6, TCP, PAY4), 12262306a36Sopenharmony_ci [0xDD] = WX_PTT(IP, IPV6, IG, IPV6, SCTP, PAY4), 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* IPv6 --> GRE/NAT -> MAC -> NONE/IPv4/IPv6 */ 12562306a36Sopenharmony_ci [0xE0] = WX_PTT(IP, IPV6, IGM, NONE, NONE, PAY3), 12662306a36Sopenharmony_ci [0xE1] = WX_PTT(IP, IPV6, IGM, FGV4, NONE, PAY3), 12762306a36Sopenharmony_ci [0xE2] = WX_PTT(IP, IPV6, IGM, IPV4, NONE, PAY3), 12862306a36Sopenharmony_ci [0xE3] = WX_PTT(IP, IPV6, IGM, IPV4, UDP, PAY4), 12962306a36Sopenharmony_ci [0xE4] = WX_PTT(IP, IPV6, IGM, IPV4, TCP, PAY4), 13062306a36Sopenharmony_ci [0xE5] = WX_PTT(IP, IPV6, IGM, IPV4, SCTP, PAY4), 13162306a36Sopenharmony_ci [0xE9] = WX_PTT(IP, IPV6, IGM, FGV6, NONE, PAY3), 13262306a36Sopenharmony_ci [0xEA] = WX_PTT(IP, IPV6, IGM, IPV6, NONE, PAY3), 13362306a36Sopenharmony_ci [0xEB] = WX_PTT(IP, IPV6, IGM, IPV6, UDP, PAY4), 13462306a36Sopenharmony_ci [0xEC] = WX_PTT(IP, IPV6, IGM, IPV6, TCP, PAY4), 13562306a36Sopenharmony_ci [0xED] = WX_PTT(IP, IPV6, IGM, IPV6, SCTP, PAY4), 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* IPv6 --> GRE/NAT -> MAC--> NONE/IPv */ 13862306a36Sopenharmony_ci [0xF0] = WX_PTT(IP, IPV6, IGMV, NONE, NONE, PAY3), 13962306a36Sopenharmony_ci [0xF1] = WX_PTT(IP, IPV6, IGMV, FGV4, NONE, PAY3), 14062306a36Sopenharmony_ci [0xF2] = WX_PTT(IP, IPV6, IGMV, IPV4, NONE, PAY3), 14162306a36Sopenharmony_ci [0xF3] = WX_PTT(IP, IPV6, IGMV, IPV4, UDP, PAY4), 14262306a36Sopenharmony_ci [0xF4] = WX_PTT(IP, IPV6, IGMV, IPV4, TCP, PAY4), 14362306a36Sopenharmony_ci [0xF5] = WX_PTT(IP, IPV6, IGMV, IPV4, SCTP, PAY4), 14462306a36Sopenharmony_ci [0xF9] = WX_PTT(IP, IPV6, IGMV, FGV6, NONE, PAY3), 14562306a36Sopenharmony_ci [0xFA] = WX_PTT(IP, IPV6, IGMV, IPV6, NONE, PAY3), 14662306a36Sopenharmony_ci [0xFB] = WX_PTT(IP, IPV6, IGMV, IPV6, UDP, PAY4), 14762306a36Sopenharmony_ci [0xFC] = WX_PTT(IP, IPV6, IGMV, IPV6, TCP, PAY4), 14862306a36Sopenharmony_ci [0xFD] = WX_PTT(IP, IPV6, IGMV, IPV6, SCTP, PAY4), 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct wx_dec_ptype wx_decode_ptype(const u8 ptype) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci return wx_ptype_lookup[ptype]; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* wx_test_staterr - tests bits in Rx descriptor status and error fields */ 15762306a36Sopenharmony_cistatic __le32 wx_test_staterr(union wx_rx_desc *rx_desc, 15862306a36Sopenharmony_ci const u32 stat_err_bits) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci return rx_desc->wb.upper.status_error & cpu_to_le32(stat_err_bits); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void wx_dma_sync_frag(struct wx_ring *rx_ring, 16462306a36Sopenharmony_ci struct wx_rx_buffer *rx_buffer) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct sk_buff *skb = rx_buffer->skb; 16762306a36Sopenharmony_ci skb_frag_t *frag = &skb_shinfo(skb)->frags[0]; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci dma_sync_single_range_for_cpu(rx_ring->dev, 17062306a36Sopenharmony_ci WX_CB(skb)->dma, 17162306a36Sopenharmony_ci skb_frag_off(frag), 17262306a36Sopenharmony_ci skb_frag_size(frag), 17362306a36Sopenharmony_ci DMA_FROM_DEVICE); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* If the page was released, just unmap it. */ 17662306a36Sopenharmony_ci if (unlikely(WX_CB(skb)->page_released)) 17762306a36Sopenharmony_ci page_pool_put_full_page(rx_ring->page_pool, rx_buffer->page, false); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct wx_rx_buffer *wx_get_rx_buffer(struct wx_ring *rx_ring, 18162306a36Sopenharmony_ci union wx_rx_desc *rx_desc, 18262306a36Sopenharmony_ci struct sk_buff **skb, 18362306a36Sopenharmony_ci int *rx_buffer_pgcnt) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct wx_rx_buffer *rx_buffer; 18662306a36Sopenharmony_ci unsigned int size; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; 18962306a36Sopenharmony_ci size = le16_to_cpu(rx_desc->wb.upper.length); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci#if (PAGE_SIZE < 8192) 19262306a36Sopenharmony_ci *rx_buffer_pgcnt = page_count(rx_buffer->page); 19362306a36Sopenharmony_ci#else 19462306a36Sopenharmony_ci *rx_buffer_pgcnt = 0; 19562306a36Sopenharmony_ci#endif 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci prefetchw(rx_buffer->page); 19862306a36Sopenharmony_ci *skb = rx_buffer->skb; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Delay unmapping of the first packet. It carries the header 20162306a36Sopenharmony_ci * information, HW may still access the header after the writeback. 20262306a36Sopenharmony_ci * Only unmap it when EOP is reached 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci if (!wx_test_staterr(rx_desc, WX_RXD_STAT_EOP)) { 20562306a36Sopenharmony_ci if (!*skb) 20662306a36Sopenharmony_ci goto skip_sync; 20762306a36Sopenharmony_ci } else { 20862306a36Sopenharmony_ci if (*skb) 20962306a36Sopenharmony_ci wx_dma_sync_frag(rx_ring, rx_buffer); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* we are reusing so sync this buffer for CPU use */ 21362306a36Sopenharmony_ci dma_sync_single_range_for_cpu(rx_ring->dev, 21462306a36Sopenharmony_ci rx_buffer->dma, 21562306a36Sopenharmony_ci rx_buffer->page_offset, 21662306a36Sopenharmony_ci size, 21762306a36Sopenharmony_ci DMA_FROM_DEVICE); 21862306a36Sopenharmony_ciskip_sync: 21962306a36Sopenharmony_ci return rx_buffer; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void wx_put_rx_buffer(struct wx_ring *rx_ring, 22362306a36Sopenharmony_ci struct wx_rx_buffer *rx_buffer, 22462306a36Sopenharmony_ci struct sk_buff *skb, 22562306a36Sopenharmony_ci int rx_buffer_pgcnt) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci if (!IS_ERR(skb) && WX_CB(skb)->dma == rx_buffer->dma) 22862306a36Sopenharmony_ci /* the page has been released from the ring */ 22962306a36Sopenharmony_ci WX_CB(skb)->page_released = true; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* clear contents of rx_buffer */ 23262306a36Sopenharmony_ci rx_buffer->page = NULL; 23362306a36Sopenharmony_ci rx_buffer->skb = NULL; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic struct sk_buff *wx_build_skb(struct wx_ring *rx_ring, 23762306a36Sopenharmony_ci struct wx_rx_buffer *rx_buffer, 23862306a36Sopenharmony_ci union wx_rx_desc *rx_desc) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci unsigned int size = le16_to_cpu(rx_desc->wb.upper.length); 24162306a36Sopenharmony_ci#if (PAGE_SIZE < 8192) 24262306a36Sopenharmony_ci unsigned int truesize = WX_RX_BUFSZ; 24362306a36Sopenharmony_ci#else 24462306a36Sopenharmony_ci unsigned int truesize = ALIGN(size, L1_CACHE_BYTES); 24562306a36Sopenharmony_ci#endif 24662306a36Sopenharmony_ci struct sk_buff *skb = rx_buffer->skb; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!skb) { 24962306a36Sopenharmony_ci void *page_addr = page_address(rx_buffer->page) + 25062306a36Sopenharmony_ci rx_buffer->page_offset; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* prefetch first cache line of first page */ 25362306a36Sopenharmony_ci prefetch(page_addr); 25462306a36Sopenharmony_ci#if L1_CACHE_BYTES < 128 25562306a36Sopenharmony_ci prefetch(page_addr + L1_CACHE_BYTES); 25662306a36Sopenharmony_ci#endif 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* allocate a skb to store the frags */ 25962306a36Sopenharmony_ci skb = napi_alloc_skb(&rx_ring->q_vector->napi, WX_RXBUFFER_256); 26062306a36Sopenharmony_ci if (unlikely(!skb)) 26162306a36Sopenharmony_ci return NULL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* we will be copying header into skb->data in 26462306a36Sopenharmony_ci * pskb_may_pull so it is in our interest to prefetch 26562306a36Sopenharmony_ci * it now to avoid a possible cache miss 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci prefetchw(skb->data); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (size <= WX_RXBUFFER_256) { 27062306a36Sopenharmony_ci memcpy(__skb_put(skb, size), page_addr, 27162306a36Sopenharmony_ci ALIGN(size, sizeof(long))); 27262306a36Sopenharmony_ci page_pool_put_full_page(rx_ring->page_pool, rx_buffer->page, true); 27362306a36Sopenharmony_ci return skb; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci skb_mark_for_recycle(skb); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (!wx_test_staterr(rx_desc, WX_RXD_STAT_EOP)) 27962306a36Sopenharmony_ci WX_CB(skb)->dma = rx_buffer->dma; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci skb_add_rx_frag(skb, 0, rx_buffer->page, 28262306a36Sopenharmony_ci rx_buffer->page_offset, 28362306a36Sopenharmony_ci size, truesize); 28462306a36Sopenharmony_ci goto out; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci } else { 28762306a36Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, 28862306a36Sopenharmony_ci rx_buffer->page_offset, size, truesize); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ciout: 29262306a36Sopenharmony_ci#if (PAGE_SIZE < 8192) 29362306a36Sopenharmony_ci /* flip page offset to other buffer */ 29462306a36Sopenharmony_ci rx_buffer->page_offset ^= truesize; 29562306a36Sopenharmony_ci#else 29662306a36Sopenharmony_ci /* move offset up to the next cache line */ 29762306a36Sopenharmony_ci rx_buffer->page_offset += truesize; 29862306a36Sopenharmony_ci#endif 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return skb; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic bool wx_alloc_mapped_page(struct wx_ring *rx_ring, 30462306a36Sopenharmony_ci struct wx_rx_buffer *bi) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct page *page = bi->page; 30762306a36Sopenharmony_ci dma_addr_t dma; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* since we are recycling buffers we should seldom need to alloc */ 31062306a36Sopenharmony_ci if (likely(page)) 31162306a36Sopenharmony_ci return true; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci page = page_pool_dev_alloc_pages(rx_ring->page_pool); 31462306a36Sopenharmony_ci WARN_ON(!page); 31562306a36Sopenharmony_ci dma = page_pool_get_dma_addr(page); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci bi->page_dma = dma; 31862306a36Sopenharmony_ci bi->page = page; 31962306a36Sopenharmony_ci bi->page_offset = 0; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return true; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/** 32562306a36Sopenharmony_ci * wx_alloc_rx_buffers - Replace used receive buffers 32662306a36Sopenharmony_ci * @rx_ring: ring to place buffers on 32762306a36Sopenharmony_ci * @cleaned_count: number of buffers to replace 32862306a36Sopenharmony_ci **/ 32962306a36Sopenharmony_civoid wx_alloc_rx_buffers(struct wx_ring *rx_ring, u16 cleaned_count) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci u16 i = rx_ring->next_to_use; 33262306a36Sopenharmony_ci union wx_rx_desc *rx_desc; 33362306a36Sopenharmony_ci struct wx_rx_buffer *bi; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* nothing to do */ 33662306a36Sopenharmony_ci if (!cleaned_count) 33762306a36Sopenharmony_ci return; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci rx_desc = WX_RX_DESC(rx_ring, i); 34062306a36Sopenharmony_ci bi = &rx_ring->rx_buffer_info[i]; 34162306a36Sopenharmony_ci i -= rx_ring->count; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci do { 34462306a36Sopenharmony_ci if (!wx_alloc_mapped_page(rx_ring, bi)) 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* sync the buffer for use by the device */ 34862306a36Sopenharmony_ci dma_sync_single_range_for_device(rx_ring->dev, bi->dma, 34962306a36Sopenharmony_ci bi->page_offset, 35062306a36Sopenharmony_ci WX_RX_BUFSZ, 35162306a36Sopenharmony_ci DMA_FROM_DEVICE); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci rx_desc->read.pkt_addr = 35462306a36Sopenharmony_ci cpu_to_le64(bi->page_dma + bi->page_offset); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci rx_desc++; 35762306a36Sopenharmony_ci bi++; 35862306a36Sopenharmony_ci i++; 35962306a36Sopenharmony_ci if (unlikely(!i)) { 36062306a36Sopenharmony_ci rx_desc = WX_RX_DESC(rx_ring, 0); 36162306a36Sopenharmony_ci bi = rx_ring->rx_buffer_info; 36262306a36Sopenharmony_ci i -= rx_ring->count; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* clear the status bits for the next_to_use descriptor */ 36662306a36Sopenharmony_ci rx_desc->wb.upper.status_error = 0; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci cleaned_count--; 36962306a36Sopenharmony_ci } while (cleaned_count); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci i += rx_ring->count; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (rx_ring->next_to_use != i) { 37462306a36Sopenharmony_ci rx_ring->next_to_use = i; 37562306a36Sopenharmony_ci /* update next to alloc since we have filled the ring */ 37662306a36Sopenharmony_ci rx_ring->next_to_alloc = i; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Force memory writes to complete before letting h/w 37962306a36Sopenharmony_ci * know there are new descriptors to fetch. (Only 38062306a36Sopenharmony_ci * applicable for weak-ordered memory model archs, 38162306a36Sopenharmony_ci * such as IA-64). 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci wmb(); 38462306a36Sopenharmony_ci writel(i, rx_ring->tail); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ciu16 wx_desc_unused(struct wx_ring *ring) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci u16 ntc = ring->next_to_clean; 39162306a36Sopenharmony_ci u16 ntu = ring->next_to_use; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/** 39762306a36Sopenharmony_ci * wx_is_non_eop - process handling of non-EOP buffers 39862306a36Sopenharmony_ci * @rx_ring: Rx ring being processed 39962306a36Sopenharmony_ci * @rx_desc: Rx descriptor for current buffer 40062306a36Sopenharmony_ci * @skb: Current socket buffer containing buffer in progress 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * This function updates next to clean. If the buffer is an EOP buffer 40362306a36Sopenharmony_ci * this function exits returning false, otherwise it will place the 40462306a36Sopenharmony_ci * sk_buff in the next buffer to be chained and return true indicating 40562306a36Sopenharmony_ci * that this is in fact a non-EOP buffer. 40662306a36Sopenharmony_ci **/ 40762306a36Sopenharmony_cistatic bool wx_is_non_eop(struct wx_ring *rx_ring, 40862306a36Sopenharmony_ci union wx_rx_desc *rx_desc, 40962306a36Sopenharmony_ci struct sk_buff *skb) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci u32 ntc = rx_ring->next_to_clean + 1; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* fetch, update, and store next to clean */ 41462306a36Sopenharmony_ci ntc = (ntc < rx_ring->count) ? ntc : 0; 41562306a36Sopenharmony_ci rx_ring->next_to_clean = ntc; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci prefetch(WX_RX_DESC(rx_ring, ntc)); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* if we are the last buffer then there is nothing else to do */ 42062306a36Sopenharmony_ci if (likely(wx_test_staterr(rx_desc, WX_RXD_STAT_EOP))) 42162306a36Sopenharmony_ci return false; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci rx_ring->rx_buffer_info[ntc].skb = skb; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return true; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void wx_pull_tail(struct sk_buff *skb) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci skb_frag_t *frag = &skb_shinfo(skb)->frags[0]; 43162306a36Sopenharmony_ci unsigned int pull_len; 43262306a36Sopenharmony_ci unsigned char *va; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* it is valid to use page_address instead of kmap since we are 43562306a36Sopenharmony_ci * working with pages allocated out of the lomem pool per 43662306a36Sopenharmony_ci * alloc_page(GFP_ATOMIC) 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci va = skb_frag_address(frag); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* we need the header to contain the greater of either ETH_HLEN or 44162306a36Sopenharmony_ci * 60 bytes if the skb->len is less than 60 for skb_pad. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ci pull_len = eth_get_headlen(skb->dev, va, WX_RXBUFFER_256); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* align pull length to size of long to optimize memcpy performance */ 44662306a36Sopenharmony_ci skb_copy_to_linear_data(skb, va, ALIGN(pull_len, sizeof(long))); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* update all of the pointers */ 44962306a36Sopenharmony_ci skb_frag_size_sub(frag, pull_len); 45062306a36Sopenharmony_ci skb_frag_off_add(frag, pull_len); 45162306a36Sopenharmony_ci skb->data_len -= pull_len; 45262306a36Sopenharmony_ci skb->tail += pull_len; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/** 45662306a36Sopenharmony_ci * wx_cleanup_headers - Correct corrupted or empty headers 45762306a36Sopenharmony_ci * @rx_ring: rx descriptor ring packet is being transacted on 45862306a36Sopenharmony_ci * @rx_desc: pointer to the EOP Rx descriptor 45962306a36Sopenharmony_ci * @skb: pointer to current skb being fixed 46062306a36Sopenharmony_ci * 46162306a36Sopenharmony_ci * Check for corrupted packet headers caused by senders on the local L2 46262306a36Sopenharmony_ci * embedded NIC switch not setting up their Tx Descriptors right. These 46362306a36Sopenharmony_ci * should be very rare. 46462306a36Sopenharmony_ci * 46562306a36Sopenharmony_ci * Also address the case where we are pulling data in on pages only 46662306a36Sopenharmony_ci * and as such no data is present in the skb header. 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * In addition if skb is not at least 60 bytes we need to pad it so that 46962306a36Sopenharmony_ci * it is large enough to qualify as a valid Ethernet frame. 47062306a36Sopenharmony_ci * 47162306a36Sopenharmony_ci * Returns true if an error was encountered and skb was freed. 47262306a36Sopenharmony_ci **/ 47362306a36Sopenharmony_cistatic bool wx_cleanup_headers(struct wx_ring *rx_ring, 47462306a36Sopenharmony_ci union wx_rx_desc *rx_desc, 47562306a36Sopenharmony_ci struct sk_buff *skb) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct net_device *netdev = rx_ring->netdev; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* verify that the packet does not have any known errors */ 48062306a36Sopenharmony_ci if (!netdev || 48162306a36Sopenharmony_ci unlikely(wx_test_staterr(rx_desc, WX_RXD_ERR_RXE) && 48262306a36Sopenharmony_ci !(netdev->features & NETIF_F_RXALL))) { 48362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 48462306a36Sopenharmony_ci return true; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* place header in linear portion of buffer */ 48862306a36Sopenharmony_ci if (!skb_headlen(skb)) 48962306a36Sopenharmony_ci wx_pull_tail(skb); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* if eth_skb_pad returns an error the skb was freed */ 49262306a36Sopenharmony_ci if (eth_skb_pad(skb)) 49362306a36Sopenharmony_ci return true; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return false; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic void wx_rx_hash(struct wx_ring *ring, 49962306a36Sopenharmony_ci union wx_rx_desc *rx_desc, 50062306a36Sopenharmony_ci struct sk_buff *skb) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci u16 rss_type; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (!(ring->netdev->features & NETIF_F_RXHASH)) 50562306a36Sopenharmony_ci return; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci rss_type = le16_to_cpu(rx_desc->wb.lower.lo_dword.hs_rss.pkt_info) & 50862306a36Sopenharmony_ci WX_RXD_RSSTYPE_MASK; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (!rss_type) 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci skb_set_hash(skb, le32_to_cpu(rx_desc->wb.lower.hi_dword.rss), 51462306a36Sopenharmony_ci (WX_RSS_L4_TYPES_MASK & (1ul << rss_type)) ? 51562306a36Sopenharmony_ci PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/** 51962306a36Sopenharmony_ci * wx_rx_checksum - indicate in skb if hw indicated a good cksum 52062306a36Sopenharmony_ci * @ring: structure containing ring specific data 52162306a36Sopenharmony_ci * @rx_desc: current Rx descriptor being processed 52262306a36Sopenharmony_ci * @skb: skb currently being received and modified 52362306a36Sopenharmony_ci **/ 52462306a36Sopenharmony_cistatic void wx_rx_checksum(struct wx_ring *ring, 52562306a36Sopenharmony_ci union wx_rx_desc *rx_desc, 52662306a36Sopenharmony_ci struct sk_buff *skb) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct wx_dec_ptype dptype = wx_decode_ptype(WX_RXD_PKTTYPE(rx_desc)); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci skb_checksum_none_assert(skb); 53162306a36Sopenharmony_ci /* Rx csum disabled */ 53262306a36Sopenharmony_ci if (!(ring->netdev->features & NETIF_F_RXCSUM)) 53362306a36Sopenharmony_ci return; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* if IPv4 header checksum error */ 53662306a36Sopenharmony_ci if ((wx_test_staterr(rx_desc, WX_RXD_STAT_IPCS) && 53762306a36Sopenharmony_ci wx_test_staterr(rx_desc, WX_RXD_ERR_IPE)) || 53862306a36Sopenharmony_ci (wx_test_staterr(rx_desc, WX_RXD_STAT_OUTERIPCS) && 53962306a36Sopenharmony_ci wx_test_staterr(rx_desc, WX_RXD_ERR_OUTERIPER))) { 54062306a36Sopenharmony_ci ring->rx_stats.csum_err++; 54162306a36Sopenharmony_ci return; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* L4 checksum offload flag must set for the below code to work */ 54562306a36Sopenharmony_ci if (!wx_test_staterr(rx_desc, WX_RXD_STAT_L4CS)) 54662306a36Sopenharmony_ci return; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Hardware can't guarantee csum if IPv6 Dest Header found */ 54962306a36Sopenharmony_ci if (dptype.prot != WX_DEC_PTYPE_PROT_SCTP && WX_RXD_IPV6EX(rx_desc)) 55062306a36Sopenharmony_ci return; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* if L4 checksum error */ 55362306a36Sopenharmony_ci if (wx_test_staterr(rx_desc, WX_RXD_ERR_TCPE)) { 55462306a36Sopenharmony_ci ring->rx_stats.csum_err++; 55562306a36Sopenharmony_ci return; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* It must be a TCP or UDP or SCTP packet with a valid checksum */ 55962306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* If there is an outer header present that might contain a checksum 56262306a36Sopenharmony_ci * we need to bump the checksum level by 1 to reflect the fact that 56362306a36Sopenharmony_ci * we are indicating we validated the inner checksum. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ci if (dptype.etype >= WX_DEC_PTYPE_ETYPE_IG) 56662306a36Sopenharmony_ci __skb_incr_checksum_unnecessary(skb); 56762306a36Sopenharmony_ci ring->rx_stats.csum_good_cnt++; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic void wx_rx_vlan(struct wx_ring *ring, union wx_rx_desc *rx_desc, 57162306a36Sopenharmony_ci struct sk_buff *skb) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci u16 ethertype; 57462306a36Sopenharmony_ci u8 idx = 0; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if ((ring->netdev->features & 57762306a36Sopenharmony_ci (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX)) && 57862306a36Sopenharmony_ci wx_test_staterr(rx_desc, WX_RXD_STAT_VP)) { 57962306a36Sopenharmony_ci idx = (le16_to_cpu(rx_desc->wb.lower.lo_dword.hs_rss.pkt_info) & 58062306a36Sopenharmony_ci 0x1c0) >> 6; 58162306a36Sopenharmony_ci ethertype = ring->q_vector->wx->tpid[idx]; 58262306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ethertype), 58362306a36Sopenharmony_ci le16_to_cpu(rx_desc->wb.upper.vlan)); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci/** 58862306a36Sopenharmony_ci * wx_process_skb_fields - Populate skb header fields from Rx descriptor 58962306a36Sopenharmony_ci * @rx_ring: rx descriptor ring packet is being transacted on 59062306a36Sopenharmony_ci * @rx_desc: pointer to the EOP Rx descriptor 59162306a36Sopenharmony_ci * @skb: pointer to current skb being populated 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * This function checks the ring, descriptor, and packet information in 59462306a36Sopenharmony_ci * order to populate the hash, checksum, protocol, and 59562306a36Sopenharmony_ci * other fields within the skb. 59662306a36Sopenharmony_ci **/ 59762306a36Sopenharmony_cistatic void wx_process_skb_fields(struct wx_ring *rx_ring, 59862306a36Sopenharmony_ci union wx_rx_desc *rx_desc, 59962306a36Sopenharmony_ci struct sk_buff *skb) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci wx_rx_hash(rx_ring, rx_desc, skb); 60262306a36Sopenharmony_ci wx_rx_checksum(rx_ring, rx_desc, skb); 60362306a36Sopenharmony_ci wx_rx_vlan(rx_ring, rx_desc, skb); 60462306a36Sopenharmony_ci skb_record_rx_queue(skb, rx_ring->queue_index); 60562306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, rx_ring->netdev); 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/** 60962306a36Sopenharmony_ci * wx_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf 61062306a36Sopenharmony_ci * @q_vector: structure containing interrupt and ring information 61162306a36Sopenharmony_ci * @rx_ring: rx descriptor ring to transact packets on 61262306a36Sopenharmony_ci * @budget: Total limit on number of packets to process 61362306a36Sopenharmony_ci * 61462306a36Sopenharmony_ci * This function provides a "bounce buffer" approach to Rx interrupt 61562306a36Sopenharmony_ci * processing. The advantage to this is that on systems that have 61662306a36Sopenharmony_ci * expensive overhead for IOMMU access this provides a means of avoiding 61762306a36Sopenharmony_ci * it by maintaining the mapping of the page to the system. 61862306a36Sopenharmony_ci * 61962306a36Sopenharmony_ci * Returns amount of work completed. 62062306a36Sopenharmony_ci **/ 62162306a36Sopenharmony_cistatic int wx_clean_rx_irq(struct wx_q_vector *q_vector, 62262306a36Sopenharmony_ci struct wx_ring *rx_ring, 62362306a36Sopenharmony_ci int budget) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci unsigned int total_rx_bytes = 0, total_rx_packets = 0; 62662306a36Sopenharmony_ci u16 cleaned_count = wx_desc_unused(rx_ring); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci do { 62962306a36Sopenharmony_ci struct wx_rx_buffer *rx_buffer; 63062306a36Sopenharmony_ci union wx_rx_desc *rx_desc; 63162306a36Sopenharmony_ci struct sk_buff *skb; 63262306a36Sopenharmony_ci int rx_buffer_pgcnt; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* return some buffers to hardware, one at a time is too slow */ 63562306a36Sopenharmony_ci if (cleaned_count >= WX_RX_BUFFER_WRITE) { 63662306a36Sopenharmony_ci wx_alloc_rx_buffers(rx_ring, cleaned_count); 63762306a36Sopenharmony_ci cleaned_count = 0; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci rx_desc = WX_RX_DESC(rx_ring, rx_ring->next_to_clean); 64162306a36Sopenharmony_ci if (!wx_test_staterr(rx_desc, WX_RXD_STAT_DD)) 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* This memory barrier is needed to keep us from reading 64562306a36Sopenharmony_ci * any other fields out of the rx_desc until we know the 64662306a36Sopenharmony_ci * descriptor has been written back 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_ci dma_rmb(); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci rx_buffer = wx_get_rx_buffer(rx_ring, rx_desc, &skb, &rx_buffer_pgcnt); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* retrieve a buffer from the ring */ 65362306a36Sopenharmony_ci skb = wx_build_skb(rx_ring, rx_buffer, rx_desc); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* exit if we failed to retrieve a buffer */ 65662306a36Sopenharmony_ci if (!skb) { 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci wx_put_rx_buffer(rx_ring, rx_buffer, skb, rx_buffer_pgcnt); 66162306a36Sopenharmony_ci cleaned_count++; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* place incomplete frames back on ring for completion */ 66462306a36Sopenharmony_ci if (wx_is_non_eop(rx_ring, rx_desc, skb)) 66562306a36Sopenharmony_ci continue; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* verify the packet layout is correct */ 66862306a36Sopenharmony_ci if (wx_cleanup_headers(rx_ring, rx_desc, skb)) 66962306a36Sopenharmony_ci continue; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* probably a little skewed due to removing CRC */ 67262306a36Sopenharmony_ci total_rx_bytes += skb->len; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* populate checksum, timestamp, VLAN, and protocol */ 67562306a36Sopenharmony_ci wx_process_skb_fields(rx_ring, rx_desc, skb); 67662306a36Sopenharmony_ci napi_gro_receive(&q_vector->napi, skb); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* update budget accounting */ 67962306a36Sopenharmony_ci total_rx_packets++; 68062306a36Sopenharmony_ci } while (likely(total_rx_packets < budget)); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci u64_stats_update_begin(&rx_ring->syncp); 68362306a36Sopenharmony_ci rx_ring->stats.packets += total_rx_packets; 68462306a36Sopenharmony_ci rx_ring->stats.bytes += total_rx_bytes; 68562306a36Sopenharmony_ci u64_stats_update_end(&rx_ring->syncp); 68662306a36Sopenharmony_ci q_vector->rx.total_packets += total_rx_packets; 68762306a36Sopenharmony_ci q_vector->rx.total_bytes += total_rx_bytes; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return total_rx_packets; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic struct netdev_queue *wx_txring_txq(const struct wx_ring *ring) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci return netdev_get_tx_queue(ring->netdev, ring->queue_index); 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/** 69862306a36Sopenharmony_ci * wx_clean_tx_irq - Reclaim resources after transmit completes 69962306a36Sopenharmony_ci * @q_vector: structure containing interrupt and ring information 70062306a36Sopenharmony_ci * @tx_ring: tx ring to clean 70162306a36Sopenharmony_ci * @napi_budget: Used to determine if we are in netpoll 70262306a36Sopenharmony_ci **/ 70362306a36Sopenharmony_cistatic bool wx_clean_tx_irq(struct wx_q_vector *q_vector, 70462306a36Sopenharmony_ci struct wx_ring *tx_ring, int napi_budget) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci unsigned int budget = q_vector->wx->tx_work_limit; 70762306a36Sopenharmony_ci unsigned int total_bytes = 0, total_packets = 0; 70862306a36Sopenharmony_ci unsigned int i = tx_ring->next_to_clean; 70962306a36Sopenharmony_ci struct wx_tx_buffer *tx_buffer; 71062306a36Sopenharmony_ci union wx_tx_desc *tx_desc; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (!netif_carrier_ok(tx_ring->netdev)) 71362306a36Sopenharmony_ci return true; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci tx_buffer = &tx_ring->tx_buffer_info[i]; 71662306a36Sopenharmony_ci tx_desc = WX_TX_DESC(tx_ring, i); 71762306a36Sopenharmony_ci i -= tx_ring->count; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci do { 72062306a36Sopenharmony_ci union wx_tx_desc *eop_desc = tx_buffer->next_to_watch; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* if next_to_watch is not set then there is no work pending */ 72362306a36Sopenharmony_ci if (!eop_desc) 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* prevent any other reads prior to eop_desc */ 72762306a36Sopenharmony_ci smp_rmb(); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* if DD is not set pending work has not been completed */ 73062306a36Sopenharmony_ci if (!(eop_desc->wb.status & cpu_to_le32(WX_TXD_STAT_DD))) 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* clear next_to_watch to prevent false hangs */ 73462306a36Sopenharmony_ci tx_buffer->next_to_watch = NULL; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* update the statistics for this packet */ 73762306a36Sopenharmony_ci total_bytes += tx_buffer->bytecount; 73862306a36Sopenharmony_ci total_packets += tx_buffer->gso_segs; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* free the skb */ 74162306a36Sopenharmony_ci napi_consume_skb(tx_buffer->skb, napi_budget); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* unmap skb header data */ 74462306a36Sopenharmony_ci dma_unmap_single(tx_ring->dev, 74562306a36Sopenharmony_ci dma_unmap_addr(tx_buffer, dma), 74662306a36Sopenharmony_ci dma_unmap_len(tx_buffer, len), 74762306a36Sopenharmony_ci DMA_TO_DEVICE); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* clear tx_buffer data */ 75062306a36Sopenharmony_ci dma_unmap_len_set(tx_buffer, len, 0); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* unmap remaining buffers */ 75362306a36Sopenharmony_ci while (tx_desc != eop_desc) { 75462306a36Sopenharmony_ci tx_buffer++; 75562306a36Sopenharmony_ci tx_desc++; 75662306a36Sopenharmony_ci i++; 75762306a36Sopenharmony_ci if (unlikely(!i)) { 75862306a36Sopenharmony_ci i -= tx_ring->count; 75962306a36Sopenharmony_ci tx_buffer = tx_ring->tx_buffer_info; 76062306a36Sopenharmony_ci tx_desc = WX_TX_DESC(tx_ring, 0); 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* unmap any remaining paged data */ 76462306a36Sopenharmony_ci if (dma_unmap_len(tx_buffer, len)) { 76562306a36Sopenharmony_ci dma_unmap_page(tx_ring->dev, 76662306a36Sopenharmony_ci dma_unmap_addr(tx_buffer, dma), 76762306a36Sopenharmony_ci dma_unmap_len(tx_buffer, len), 76862306a36Sopenharmony_ci DMA_TO_DEVICE); 76962306a36Sopenharmony_ci dma_unmap_len_set(tx_buffer, len, 0); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* move us one more past the eop_desc for start of next pkt */ 77462306a36Sopenharmony_ci tx_buffer++; 77562306a36Sopenharmony_ci tx_desc++; 77662306a36Sopenharmony_ci i++; 77762306a36Sopenharmony_ci if (unlikely(!i)) { 77862306a36Sopenharmony_ci i -= tx_ring->count; 77962306a36Sopenharmony_ci tx_buffer = tx_ring->tx_buffer_info; 78062306a36Sopenharmony_ci tx_desc = WX_TX_DESC(tx_ring, 0); 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* issue prefetch for next Tx descriptor */ 78462306a36Sopenharmony_ci prefetch(tx_desc); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* update budget accounting */ 78762306a36Sopenharmony_ci budget--; 78862306a36Sopenharmony_ci } while (likely(budget)); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci i += tx_ring->count; 79162306a36Sopenharmony_ci tx_ring->next_to_clean = i; 79262306a36Sopenharmony_ci u64_stats_update_begin(&tx_ring->syncp); 79362306a36Sopenharmony_ci tx_ring->stats.bytes += total_bytes; 79462306a36Sopenharmony_ci tx_ring->stats.packets += total_packets; 79562306a36Sopenharmony_ci u64_stats_update_end(&tx_ring->syncp); 79662306a36Sopenharmony_ci q_vector->tx.total_bytes += total_bytes; 79762306a36Sopenharmony_ci q_vector->tx.total_packets += total_packets; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci netdev_tx_completed_queue(wx_txring_txq(tx_ring), 80062306a36Sopenharmony_ci total_packets, total_bytes); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) 80362306a36Sopenharmony_ci if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) && 80462306a36Sopenharmony_ci (wx_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) { 80562306a36Sopenharmony_ci /* Make sure that anybody stopping the queue after this 80662306a36Sopenharmony_ci * sees the new next_to_clean. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci smp_mb(); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (__netif_subqueue_stopped(tx_ring->netdev, 81162306a36Sopenharmony_ci tx_ring->queue_index) && 81262306a36Sopenharmony_ci netif_running(tx_ring->netdev)) 81362306a36Sopenharmony_ci netif_wake_subqueue(tx_ring->netdev, 81462306a36Sopenharmony_ci tx_ring->queue_index); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return !!budget; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci/** 82162306a36Sopenharmony_ci * wx_poll - NAPI polling RX/TX cleanup routine 82262306a36Sopenharmony_ci * @napi: napi struct with our devices info in it 82362306a36Sopenharmony_ci * @budget: amount of work driver is allowed to do this pass, in packets 82462306a36Sopenharmony_ci * 82562306a36Sopenharmony_ci * This function will clean all queues associated with a q_vector. 82662306a36Sopenharmony_ci **/ 82762306a36Sopenharmony_cistatic int wx_poll(struct napi_struct *napi, int budget) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct wx_q_vector *q_vector = container_of(napi, struct wx_q_vector, napi); 83062306a36Sopenharmony_ci int per_ring_budget, work_done = 0; 83162306a36Sopenharmony_ci struct wx *wx = q_vector->wx; 83262306a36Sopenharmony_ci bool clean_complete = true; 83362306a36Sopenharmony_ci struct wx_ring *ring; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci wx_for_each_ring(ring, q_vector->tx) { 83662306a36Sopenharmony_ci if (!wx_clean_tx_irq(q_vector, ring, budget)) 83762306a36Sopenharmony_ci clean_complete = false; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* Exit if we are called by netpoll */ 84162306a36Sopenharmony_ci if (budget <= 0) 84262306a36Sopenharmony_ci return budget; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* attempt to distribute budget to each queue fairly, but don't allow 84562306a36Sopenharmony_ci * the budget to go below 1 because we'll exit polling 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ci if (q_vector->rx.count > 1) 84862306a36Sopenharmony_ci per_ring_budget = max(budget / q_vector->rx.count, 1); 84962306a36Sopenharmony_ci else 85062306a36Sopenharmony_ci per_ring_budget = budget; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci wx_for_each_ring(ring, q_vector->rx) { 85362306a36Sopenharmony_ci int cleaned = wx_clean_rx_irq(q_vector, ring, per_ring_budget); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci work_done += cleaned; 85662306a36Sopenharmony_ci if (cleaned >= per_ring_budget) 85762306a36Sopenharmony_ci clean_complete = false; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* If all work not completed, return budget and keep polling */ 86162306a36Sopenharmony_ci if (!clean_complete) 86262306a36Sopenharmony_ci return budget; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* all work done, exit the polling mode */ 86562306a36Sopenharmony_ci if (likely(napi_complete_done(napi, work_done))) { 86662306a36Sopenharmony_ci if (netif_running(wx->netdev)) 86762306a36Sopenharmony_ci wx_intr_enable(wx, WX_INTR_Q(q_vector->v_idx)); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return min(work_done, budget - 1); 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic int wx_maybe_stop_tx(struct wx_ring *tx_ring, u16 size) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci if (likely(wx_desc_unused(tx_ring) >= size)) 87662306a36Sopenharmony_ci return 0; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* For the next check */ 88162306a36Sopenharmony_ci smp_mb(); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* We need to check again in a case another CPU has just 88462306a36Sopenharmony_ci * made room available. 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_ci if (likely(wx_desc_unused(tx_ring) < size)) 88762306a36Sopenharmony_ci return -EBUSY; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* A reprieve! - use start_queue because it doesn't call schedule */ 89062306a36Sopenharmony_ci netif_start_subqueue(tx_ring->netdev, tx_ring->queue_index); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci return 0; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic u32 wx_tx_cmd_type(u32 tx_flags) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci /* set type for advanced descriptor with frame checksum insertion */ 89862306a36Sopenharmony_ci u32 cmd_type = WX_TXD_DTYP_DATA | WX_TXD_IFCS; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* set HW vlan bit if vlan is present */ 90162306a36Sopenharmony_ci cmd_type |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_HW_VLAN, WX_TXD_VLE); 90262306a36Sopenharmony_ci /* set segmentation enable bits for TSO/FSO */ 90362306a36Sopenharmony_ci cmd_type |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_TSO, WX_TXD_TSE); 90462306a36Sopenharmony_ci /* set timestamp bit if present */ 90562306a36Sopenharmony_ci cmd_type |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_TSTAMP, WX_TXD_MAC_TSTAMP); 90662306a36Sopenharmony_ci cmd_type |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_LINKSEC, WX_TXD_LINKSEC); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci return cmd_type; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic void wx_tx_olinfo_status(union wx_tx_desc *tx_desc, 91262306a36Sopenharmony_ci u32 tx_flags, unsigned int paylen) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci u32 olinfo_status = paylen << WX_TXD_PAYLEN_SHIFT; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* enable L4 checksum for TSO and TX checksum offload */ 91762306a36Sopenharmony_ci olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_CSUM, WX_TXD_L4CS); 91862306a36Sopenharmony_ci /* enable IPv4 checksum for TSO */ 91962306a36Sopenharmony_ci olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_IPV4, WX_TXD_IIPCS); 92062306a36Sopenharmony_ci /* enable outer IPv4 checksum for TSO */ 92162306a36Sopenharmony_ci olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_OUTER_IPV4, 92262306a36Sopenharmony_ci WX_TXD_EIPCS); 92362306a36Sopenharmony_ci /* Check Context must be set if Tx switch is enabled, which it 92462306a36Sopenharmony_ci * always is for case where virtual functions are running 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_CC, WX_TXD_CC); 92762306a36Sopenharmony_ci olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_IPSEC, 92862306a36Sopenharmony_ci WX_TXD_IPSEC); 92962306a36Sopenharmony_ci tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic void wx_tx_map(struct wx_ring *tx_ring, 93362306a36Sopenharmony_ci struct wx_tx_buffer *first, 93462306a36Sopenharmony_ci const u8 hdr_len) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct sk_buff *skb = first->skb; 93762306a36Sopenharmony_ci struct wx_tx_buffer *tx_buffer; 93862306a36Sopenharmony_ci u32 tx_flags = first->tx_flags; 93962306a36Sopenharmony_ci u16 i = tx_ring->next_to_use; 94062306a36Sopenharmony_ci unsigned int data_len, size; 94162306a36Sopenharmony_ci union wx_tx_desc *tx_desc; 94262306a36Sopenharmony_ci skb_frag_t *frag; 94362306a36Sopenharmony_ci dma_addr_t dma; 94462306a36Sopenharmony_ci u32 cmd_type; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci cmd_type = wx_tx_cmd_type(tx_flags); 94762306a36Sopenharmony_ci tx_desc = WX_TX_DESC(tx_ring, i); 94862306a36Sopenharmony_ci wx_tx_olinfo_status(tx_desc, tx_flags, skb->len - hdr_len); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci size = skb_headlen(skb); 95162306a36Sopenharmony_ci data_len = skb->data_len; 95262306a36Sopenharmony_ci dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci tx_buffer = first; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci for (frag = &skb_shinfo(skb)->frags[0];; frag++) { 95762306a36Sopenharmony_ci if (dma_mapping_error(tx_ring->dev, dma)) 95862306a36Sopenharmony_ci goto dma_error; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* record length, and DMA address */ 96162306a36Sopenharmony_ci dma_unmap_len_set(tx_buffer, len, size); 96262306a36Sopenharmony_ci dma_unmap_addr_set(tx_buffer, dma, dma); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci tx_desc->read.buffer_addr = cpu_to_le64(dma); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci while (unlikely(size > WX_MAX_DATA_PER_TXD)) { 96762306a36Sopenharmony_ci tx_desc->read.cmd_type_len = 96862306a36Sopenharmony_ci cpu_to_le32(cmd_type ^ WX_MAX_DATA_PER_TXD); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci i++; 97162306a36Sopenharmony_ci tx_desc++; 97262306a36Sopenharmony_ci if (i == tx_ring->count) { 97362306a36Sopenharmony_ci tx_desc = WX_TX_DESC(tx_ring, 0); 97462306a36Sopenharmony_ci i = 0; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci tx_desc->read.olinfo_status = 0; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci dma += WX_MAX_DATA_PER_TXD; 97962306a36Sopenharmony_ci size -= WX_MAX_DATA_PER_TXD; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci tx_desc->read.buffer_addr = cpu_to_le64(dma); 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (likely(!data_len)) 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type ^ size); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci i++; 99062306a36Sopenharmony_ci tx_desc++; 99162306a36Sopenharmony_ci if (i == tx_ring->count) { 99262306a36Sopenharmony_ci tx_desc = WX_TX_DESC(tx_ring, 0); 99362306a36Sopenharmony_ci i = 0; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci tx_desc->read.olinfo_status = 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci size = skb_frag_size(frag); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci data_len -= size; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci dma = skb_frag_dma_map(tx_ring->dev, frag, 0, size, 100262306a36Sopenharmony_ci DMA_TO_DEVICE); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci tx_buffer = &tx_ring->tx_buffer_info[i]; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* write last descriptor with RS and EOP bits */ 100862306a36Sopenharmony_ci cmd_type |= size | WX_TXD_EOP | WX_TXD_RS; 100962306a36Sopenharmony_ci tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci netdev_tx_sent_queue(wx_txring_txq(tx_ring), first->bytecount); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci skb_tx_timestamp(skb); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* Force memory writes to complete before letting h/w know there 101662306a36Sopenharmony_ci * are new descriptors to fetch. (Only applicable for weak-ordered 101762306a36Sopenharmony_ci * memory model archs, such as IA-64). 101862306a36Sopenharmony_ci * 101962306a36Sopenharmony_ci * We also need this memory barrier to make certain all of the 102062306a36Sopenharmony_ci * status bits have been updated before next_to_watch is written. 102162306a36Sopenharmony_ci */ 102262306a36Sopenharmony_ci wmb(); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* set next_to_watch value indicating a packet is present */ 102562306a36Sopenharmony_ci first->next_to_watch = tx_desc; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci i++; 102862306a36Sopenharmony_ci if (i == tx_ring->count) 102962306a36Sopenharmony_ci i = 0; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci tx_ring->next_to_use = i; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci wx_maybe_stop_tx(tx_ring, DESC_NEEDED); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (netif_xmit_stopped(wx_txring_txq(tx_ring)) || !netdev_xmit_more()) 103662306a36Sopenharmony_ci writel(i, tx_ring->tail); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci return; 103962306a36Sopenharmony_cidma_error: 104062306a36Sopenharmony_ci dev_err(tx_ring->dev, "TX DMA map failed\n"); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* clear dma mappings for failed tx_buffer_info map */ 104362306a36Sopenharmony_ci for (;;) { 104462306a36Sopenharmony_ci tx_buffer = &tx_ring->tx_buffer_info[i]; 104562306a36Sopenharmony_ci if (dma_unmap_len(tx_buffer, len)) 104662306a36Sopenharmony_ci dma_unmap_page(tx_ring->dev, 104762306a36Sopenharmony_ci dma_unmap_addr(tx_buffer, dma), 104862306a36Sopenharmony_ci dma_unmap_len(tx_buffer, len), 104962306a36Sopenharmony_ci DMA_TO_DEVICE); 105062306a36Sopenharmony_ci dma_unmap_len_set(tx_buffer, len, 0); 105162306a36Sopenharmony_ci if (tx_buffer == first) 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci if (i == 0) 105462306a36Sopenharmony_ci i += tx_ring->count; 105562306a36Sopenharmony_ci i--; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci dev_kfree_skb_any(first->skb); 105962306a36Sopenharmony_ci first->skb = NULL; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci tx_ring->next_to_use = i; 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic void wx_tx_ctxtdesc(struct wx_ring *tx_ring, u32 vlan_macip_lens, 106562306a36Sopenharmony_ci u32 fcoe_sof_eof, u32 type_tucmd, u32 mss_l4len_idx) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci struct wx_tx_context_desc *context_desc; 106862306a36Sopenharmony_ci u16 i = tx_ring->next_to_use; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci context_desc = WX_TX_CTXTDESC(tx_ring, i); 107162306a36Sopenharmony_ci i++; 107262306a36Sopenharmony_ci tx_ring->next_to_use = (i < tx_ring->count) ? i : 0; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* set bits to identify this as an advanced context descriptor */ 107562306a36Sopenharmony_ci type_tucmd |= WX_TXD_DTYP_CTXT; 107662306a36Sopenharmony_ci context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens); 107762306a36Sopenharmony_ci context_desc->seqnum_seed = cpu_to_le32(fcoe_sof_eof); 107862306a36Sopenharmony_ci context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd); 107962306a36Sopenharmony_ci context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx); 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cistatic void wx_get_ipv6_proto(struct sk_buff *skb, int offset, u8 *nexthdr) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct ipv6hdr *hdr = (struct ipv6hdr *)(skb->data + offset); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci *nexthdr = hdr->nexthdr; 108762306a36Sopenharmony_ci offset += sizeof(struct ipv6hdr); 108862306a36Sopenharmony_ci while (ipv6_ext_hdr(*nexthdr)) { 108962306a36Sopenharmony_ci struct ipv6_opt_hdr _hdr, *hp; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (*nexthdr == NEXTHDR_NONE) 109262306a36Sopenharmony_ci return; 109362306a36Sopenharmony_ci hp = skb_header_pointer(skb, offset, sizeof(_hdr), &_hdr); 109462306a36Sopenharmony_ci if (!hp) 109562306a36Sopenharmony_ci return; 109662306a36Sopenharmony_ci if (*nexthdr == NEXTHDR_FRAGMENT) 109762306a36Sopenharmony_ci break; 109862306a36Sopenharmony_ci *nexthdr = hp->nexthdr; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ciunion network_header { 110362306a36Sopenharmony_ci struct iphdr *ipv4; 110462306a36Sopenharmony_ci struct ipv6hdr *ipv6; 110562306a36Sopenharmony_ci void *raw; 110662306a36Sopenharmony_ci}; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic u8 wx_encode_tx_desc_ptype(const struct wx_tx_buffer *first) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci u8 tun_prot = 0, l4_prot = 0, ptype = 0; 111162306a36Sopenharmony_ci struct sk_buff *skb = first->skb; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (skb->encapsulation) { 111462306a36Sopenharmony_ci union network_header hdr; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci switch (first->protocol) { 111762306a36Sopenharmony_ci case htons(ETH_P_IP): 111862306a36Sopenharmony_ci tun_prot = ip_hdr(skb)->protocol; 111962306a36Sopenharmony_ci ptype = WX_PTYPE_TUN_IPV4; 112062306a36Sopenharmony_ci break; 112162306a36Sopenharmony_ci case htons(ETH_P_IPV6): 112262306a36Sopenharmony_ci wx_get_ipv6_proto(skb, skb_network_offset(skb), &tun_prot); 112362306a36Sopenharmony_ci ptype = WX_PTYPE_TUN_IPV6; 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci default: 112662306a36Sopenharmony_ci return ptype; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (tun_prot == IPPROTO_IPIP) { 113062306a36Sopenharmony_ci hdr.raw = (void *)inner_ip_hdr(skb); 113162306a36Sopenharmony_ci ptype |= WX_PTYPE_PKT_IPIP; 113262306a36Sopenharmony_ci } else if (tun_prot == IPPROTO_UDP) { 113362306a36Sopenharmony_ci hdr.raw = (void *)inner_ip_hdr(skb); 113462306a36Sopenharmony_ci if (skb->inner_protocol_type != ENCAP_TYPE_ETHER || 113562306a36Sopenharmony_ci skb->inner_protocol != htons(ETH_P_TEB)) { 113662306a36Sopenharmony_ci ptype |= WX_PTYPE_PKT_IG; 113762306a36Sopenharmony_ci } else { 113862306a36Sopenharmony_ci if (((struct ethhdr *)skb_inner_mac_header(skb))->h_proto 113962306a36Sopenharmony_ci == htons(ETH_P_8021Q)) 114062306a36Sopenharmony_ci ptype |= WX_PTYPE_PKT_IGMV; 114162306a36Sopenharmony_ci else 114262306a36Sopenharmony_ci ptype |= WX_PTYPE_PKT_IGM; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci } else if (tun_prot == IPPROTO_GRE) { 114662306a36Sopenharmony_ci hdr.raw = (void *)inner_ip_hdr(skb); 114762306a36Sopenharmony_ci if (skb->inner_protocol == htons(ETH_P_IP) || 114862306a36Sopenharmony_ci skb->inner_protocol == htons(ETH_P_IPV6)) { 114962306a36Sopenharmony_ci ptype |= WX_PTYPE_PKT_IG; 115062306a36Sopenharmony_ci } else { 115162306a36Sopenharmony_ci if (((struct ethhdr *)skb_inner_mac_header(skb))->h_proto 115262306a36Sopenharmony_ci == htons(ETH_P_8021Q)) 115362306a36Sopenharmony_ci ptype |= WX_PTYPE_PKT_IGMV; 115462306a36Sopenharmony_ci else 115562306a36Sopenharmony_ci ptype |= WX_PTYPE_PKT_IGM; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci } else { 115862306a36Sopenharmony_ci return ptype; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci switch (hdr.ipv4->version) { 116262306a36Sopenharmony_ci case IPVERSION: 116362306a36Sopenharmony_ci l4_prot = hdr.ipv4->protocol; 116462306a36Sopenharmony_ci break; 116562306a36Sopenharmony_ci case 6: 116662306a36Sopenharmony_ci wx_get_ipv6_proto(skb, skb_inner_network_offset(skb), &l4_prot); 116762306a36Sopenharmony_ci ptype |= WX_PTYPE_PKT_IPV6; 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci default: 117062306a36Sopenharmony_ci return ptype; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci } else { 117362306a36Sopenharmony_ci switch (first->protocol) { 117462306a36Sopenharmony_ci case htons(ETH_P_IP): 117562306a36Sopenharmony_ci l4_prot = ip_hdr(skb)->protocol; 117662306a36Sopenharmony_ci ptype = WX_PTYPE_PKT_IP; 117762306a36Sopenharmony_ci break; 117862306a36Sopenharmony_ci case htons(ETH_P_IPV6): 117962306a36Sopenharmony_ci wx_get_ipv6_proto(skb, skb_network_offset(skb), &l4_prot); 118062306a36Sopenharmony_ci ptype = WX_PTYPE_PKT_IP | WX_PTYPE_PKT_IPV6; 118162306a36Sopenharmony_ci break; 118262306a36Sopenharmony_ci default: 118362306a36Sopenharmony_ci return WX_PTYPE_PKT_MAC | WX_PTYPE_TYP_MAC; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci switch (l4_prot) { 118762306a36Sopenharmony_ci case IPPROTO_TCP: 118862306a36Sopenharmony_ci ptype |= WX_PTYPE_TYP_TCP; 118962306a36Sopenharmony_ci break; 119062306a36Sopenharmony_ci case IPPROTO_UDP: 119162306a36Sopenharmony_ci ptype |= WX_PTYPE_TYP_UDP; 119262306a36Sopenharmony_ci break; 119362306a36Sopenharmony_ci case IPPROTO_SCTP: 119462306a36Sopenharmony_ci ptype |= WX_PTYPE_TYP_SCTP; 119562306a36Sopenharmony_ci break; 119662306a36Sopenharmony_ci default: 119762306a36Sopenharmony_ci ptype |= WX_PTYPE_TYP_IP; 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return ptype; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic int wx_tso(struct wx_ring *tx_ring, struct wx_tx_buffer *first, 120562306a36Sopenharmony_ci u8 *hdr_len, u8 ptype) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci u32 vlan_macip_lens, type_tucmd, mss_l4len_idx; 120862306a36Sopenharmony_ci struct net_device *netdev = tx_ring->netdev; 120962306a36Sopenharmony_ci u32 l4len, tunhdr_eiplen_tunlen = 0; 121062306a36Sopenharmony_ci struct sk_buff *skb = first->skb; 121162306a36Sopenharmony_ci bool enc = skb->encapsulation; 121262306a36Sopenharmony_ci struct ipv6hdr *ipv6h; 121362306a36Sopenharmony_ci struct tcphdr *tcph; 121462306a36Sopenharmony_ci struct iphdr *iph; 121562306a36Sopenharmony_ci u8 tun_prot = 0; 121662306a36Sopenharmony_ci int err; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (!skb_is_gso(skb)) 122262306a36Sopenharmony_ci return 0; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci err = skb_cow_head(skb, 0); 122562306a36Sopenharmony_ci if (err < 0) 122662306a36Sopenharmony_ci return err; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* indicates the inner headers in the skbuff are valid. */ 122962306a36Sopenharmony_ci iph = enc ? inner_ip_hdr(skb) : ip_hdr(skb); 123062306a36Sopenharmony_ci if (iph->version == 4) { 123162306a36Sopenharmony_ci tcph = enc ? inner_tcp_hdr(skb) : tcp_hdr(skb); 123262306a36Sopenharmony_ci iph->tot_len = 0; 123362306a36Sopenharmony_ci iph->check = 0; 123462306a36Sopenharmony_ci tcph->check = ~csum_tcpudp_magic(iph->saddr, 123562306a36Sopenharmony_ci iph->daddr, 0, 123662306a36Sopenharmony_ci IPPROTO_TCP, 0); 123762306a36Sopenharmony_ci first->tx_flags |= WX_TX_FLAGS_TSO | 123862306a36Sopenharmony_ci WX_TX_FLAGS_CSUM | 123962306a36Sopenharmony_ci WX_TX_FLAGS_IPV4 | 124062306a36Sopenharmony_ci WX_TX_FLAGS_CC; 124162306a36Sopenharmony_ci } else if (iph->version == 6 && skb_is_gso_v6(skb)) { 124262306a36Sopenharmony_ci ipv6h = enc ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); 124362306a36Sopenharmony_ci tcph = enc ? inner_tcp_hdr(skb) : tcp_hdr(skb); 124462306a36Sopenharmony_ci ipv6h->payload_len = 0; 124562306a36Sopenharmony_ci tcph->check = ~csum_ipv6_magic(&ipv6h->saddr, 124662306a36Sopenharmony_ci &ipv6h->daddr, 0, 124762306a36Sopenharmony_ci IPPROTO_TCP, 0); 124862306a36Sopenharmony_ci first->tx_flags |= WX_TX_FLAGS_TSO | 124962306a36Sopenharmony_ci WX_TX_FLAGS_CSUM | 125062306a36Sopenharmony_ci WX_TX_FLAGS_CC; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* compute header lengths */ 125462306a36Sopenharmony_ci l4len = enc ? inner_tcp_hdrlen(skb) : tcp_hdrlen(skb); 125562306a36Sopenharmony_ci *hdr_len = enc ? (skb_inner_transport_header(skb) - skb->data) : 125662306a36Sopenharmony_ci skb_transport_offset(skb); 125762306a36Sopenharmony_ci *hdr_len += l4len; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* update gso size and bytecount with header size */ 126062306a36Sopenharmony_ci first->gso_segs = skb_shinfo(skb)->gso_segs; 126162306a36Sopenharmony_ci first->bytecount += (first->gso_segs - 1) * *hdr_len; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci /* mss_l4len_id: use 0 as index for TSO */ 126462306a36Sopenharmony_ci mss_l4len_idx = l4len << WX_TXD_L4LEN_SHIFT; 126562306a36Sopenharmony_ci mss_l4len_idx |= skb_shinfo(skb)->gso_size << WX_TXD_MSS_SHIFT; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* vlan_macip_lens: HEADLEN, MACLEN, VLAN tag */ 126862306a36Sopenharmony_ci if (enc) { 126962306a36Sopenharmony_ci switch (first->protocol) { 127062306a36Sopenharmony_ci case htons(ETH_P_IP): 127162306a36Sopenharmony_ci tun_prot = ip_hdr(skb)->protocol; 127262306a36Sopenharmony_ci first->tx_flags |= WX_TX_FLAGS_OUTER_IPV4; 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci case htons(ETH_P_IPV6): 127562306a36Sopenharmony_ci tun_prot = ipv6_hdr(skb)->nexthdr; 127662306a36Sopenharmony_ci break; 127762306a36Sopenharmony_ci default: 127862306a36Sopenharmony_ci break; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci switch (tun_prot) { 128162306a36Sopenharmony_ci case IPPROTO_UDP: 128262306a36Sopenharmony_ci tunhdr_eiplen_tunlen = WX_TXD_TUNNEL_UDP; 128362306a36Sopenharmony_ci tunhdr_eiplen_tunlen |= ((skb_network_header_len(skb) >> 2) << 128462306a36Sopenharmony_ci WX_TXD_OUTER_IPLEN_SHIFT) | 128562306a36Sopenharmony_ci (((skb_inner_mac_header(skb) - 128662306a36Sopenharmony_ci skb_transport_header(skb)) >> 1) << 128762306a36Sopenharmony_ci WX_TXD_TUNNEL_LEN_SHIFT); 128862306a36Sopenharmony_ci break; 128962306a36Sopenharmony_ci case IPPROTO_GRE: 129062306a36Sopenharmony_ci tunhdr_eiplen_tunlen = WX_TXD_TUNNEL_GRE; 129162306a36Sopenharmony_ci tunhdr_eiplen_tunlen |= ((skb_network_header_len(skb) >> 2) << 129262306a36Sopenharmony_ci WX_TXD_OUTER_IPLEN_SHIFT) | 129362306a36Sopenharmony_ci (((skb_inner_mac_header(skb) - 129462306a36Sopenharmony_ci skb_transport_header(skb)) >> 1) << 129562306a36Sopenharmony_ci WX_TXD_TUNNEL_LEN_SHIFT); 129662306a36Sopenharmony_ci break; 129762306a36Sopenharmony_ci case IPPROTO_IPIP: 129862306a36Sopenharmony_ci tunhdr_eiplen_tunlen = (((char *)inner_ip_hdr(skb) - 129962306a36Sopenharmony_ci (char *)ip_hdr(skb)) >> 2) << 130062306a36Sopenharmony_ci WX_TXD_OUTER_IPLEN_SHIFT; 130162306a36Sopenharmony_ci break; 130262306a36Sopenharmony_ci default: 130362306a36Sopenharmony_ci break; 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci vlan_macip_lens = skb_inner_network_header_len(skb) >> 1; 130662306a36Sopenharmony_ci } else { 130762306a36Sopenharmony_ci vlan_macip_lens = skb_network_header_len(skb) >> 1; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci vlan_macip_lens |= skb_network_offset(skb) << WX_TXD_MACLEN_SHIFT; 131162306a36Sopenharmony_ci vlan_macip_lens |= first->tx_flags & WX_TX_FLAGS_VLAN_MASK; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci type_tucmd = ptype << 24; 131462306a36Sopenharmony_ci if (skb->vlan_proto == htons(ETH_P_8021AD) && 131562306a36Sopenharmony_ci netdev->features & NETIF_F_HW_VLAN_STAG_TX) 131662306a36Sopenharmony_ci type_tucmd |= WX_SET_FLAG(first->tx_flags, 131762306a36Sopenharmony_ci WX_TX_FLAGS_HW_VLAN, 131862306a36Sopenharmony_ci 0x1 << WX_TXD_TAG_TPID_SEL_SHIFT); 131962306a36Sopenharmony_ci wx_tx_ctxtdesc(tx_ring, vlan_macip_lens, tunhdr_eiplen_tunlen, 132062306a36Sopenharmony_ci type_tucmd, mss_l4len_idx); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci return 1; 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic void wx_tx_csum(struct wx_ring *tx_ring, struct wx_tx_buffer *first, 132662306a36Sopenharmony_ci u8 ptype) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci u32 tunhdr_eiplen_tunlen = 0, vlan_macip_lens = 0; 132962306a36Sopenharmony_ci struct net_device *netdev = tx_ring->netdev; 133062306a36Sopenharmony_ci u32 mss_l4len_idx = 0, type_tucmd; 133162306a36Sopenharmony_ci struct sk_buff *skb = first->skb; 133262306a36Sopenharmony_ci u8 tun_prot = 0; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) { 133562306a36Sopenharmony_ci if (!(first->tx_flags & WX_TX_FLAGS_HW_VLAN) && 133662306a36Sopenharmony_ci !(first->tx_flags & WX_TX_FLAGS_CC)) 133762306a36Sopenharmony_ci return; 133862306a36Sopenharmony_ci vlan_macip_lens = skb_network_offset(skb) << 133962306a36Sopenharmony_ci WX_TXD_MACLEN_SHIFT; 134062306a36Sopenharmony_ci } else { 134162306a36Sopenharmony_ci u8 l4_prot = 0; 134262306a36Sopenharmony_ci union { 134362306a36Sopenharmony_ci struct iphdr *ipv4; 134462306a36Sopenharmony_ci struct ipv6hdr *ipv6; 134562306a36Sopenharmony_ci u8 *raw; 134662306a36Sopenharmony_ci } network_hdr; 134762306a36Sopenharmony_ci union { 134862306a36Sopenharmony_ci struct tcphdr *tcphdr; 134962306a36Sopenharmony_ci u8 *raw; 135062306a36Sopenharmony_ci } transport_hdr; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (skb->encapsulation) { 135362306a36Sopenharmony_ci network_hdr.raw = skb_inner_network_header(skb); 135462306a36Sopenharmony_ci transport_hdr.raw = skb_inner_transport_header(skb); 135562306a36Sopenharmony_ci vlan_macip_lens = skb_network_offset(skb) << 135662306a36Sopenharmony_ci WX_TXD_MACLEN_SHIFT; 135762306a36Sopenharmony_ci switch (first->protocol) { 135862306a36Sopenharmony_ci case htons(ETH_P_IP): 135962306a36Sopenharmony_ci tun_prot = ip_hdr(skb)->protocol; 136062306a36Sopenharmony_ci break; 136162306a36Sopenharmony_ci case htons(ETH_P_IPV6): 136262306a36Sopenharmony_ci tun_prot = ipv6_hdr(skb)->nexthdr; 136362306a36Sopenharmony_ci break; 136462306a36Sopenharmony_ci default: 136562306a36Sopenharmony_ci return; 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci switch (tun_prot) { 136862306a36Sopenharmony_ci case IPPROTO_UDP: 136962306a36Sopenharmony_ci tunhdr_eiplen_tunlen = WX_TXD_TUNNEL_UDP; 137062306a36Sopenharmony_ci tunhdr_eiplen_tunlen |= 137162306a36Sopenharmony_ci ((skb_network_header_len(skb) >> 2) << 137262306a36Sopenharmony_ci WX_TXD_OUTER_IPLEN_SHIFT) | 137362306a36Sopenharmony_ci (((skb_inner_mac_header(skb) - 137462306a36Sopenharmony_ci skb_transport_header(skb)) >> 1) << 137562306a36Sopenharmony_ci WX_TXD_TUNNEL_LEN_SHIFT); 137662306a36Sopenharmony_ci break; 137762306a36Sopenharmony_ci case IPPROTO_GRE: 137862306a36Sopenharmony_ci tunhdr_eiplen_tunlen = WX_TXD_TUNNEL_GRE; 137962306a36Sopenharmony_ci tunhdr_eiplen_tunlen |= ((skb_network_header_len(skb) >> 2) << 138062306a36Sopenharmony_ci WX_TXD_OUTER_IPLEN_SHIFT) | 138162306a36Sopenharmony_ci (((skb_inner_mac_header(skb) - 138262306a36Sopenharmony_ci skb_transport_header(skb)) >> 1) << 138362306a36Sopenharmony_ci WX_TXD_TUNNEL_LEN_SHIFT); 138462306a36Sopenharmony_ci break; 138562306a36Sopenharmony_ci case IPPROTO_IPIP: 138662306a36Sopenharmony_ci tunhdr_eiplen_tunlen = (((char *)inner_ip_hdr(skb) - 138762306a36Sopenharmony_ci (char *)ip_hdr(skb)) >> 2) << 138862306a36Sopenharmony_ci WX_TXD_OUTER_IPLEN_SHIFT; 138962306a36Sopenharmony_ci break; 139062306a36Sopenharmony_ci default: 139162306a36Sopenharmony_ci break; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci } else { 139562306a36Sopenharmony_ci network_hdr.raw = skb_network_header(skb); 139662306a36Sopenharmony_ci transport_hdr.raw = skb_transport_header(skb); 139762306a36Sopenharmony_ci vlan_macip_lens = skb_network_offset(skb) << 139862306a36Sopenharmony_ci WX_TXD_MACLEN_SHIFT; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci switch (network_hdr.ipv4->version) { 140262306a36Sopenharmony_ci case IPVERSION: 140362306a36Sopenharmony_ci vlan_macip_lens |= (transport_hdr.raw - network_hdr.raw) >> 1; 140462306a36Sopenharmony_ci l4_prot = network_hdr.ipv4->protocol; 140562306a36Sopenharmony_ci break; 140662306a36Sopenharmony_ci case 6: 140762306a36Sopenharmony_ci vlan_macip_lens |= (transport_hdr.raw - network_hdr.raw) >> 1; 140862306a36Sopenharmony_ci l4_prot = network_hdr.ipv6->nexthdr; 140962306a36Sopenharmony_ci break; 141062306a36Sopenharmony_ci default: 141162306a36Sopenharmony_ci break; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci switch (l4_prot) { 141562306a36Sopenharmony_ci case IPPROTO_TCP: 141662306a36Sopenharmony_ci mss_l4len_idx = (transport_hdr.tcphdr->doff * 4) << 141762306a36Sopenharmony_ci WX_TXD_L4LEN_SHIFT; 141862306a36Sopenharmony_ci break; 141962306a36Sopenharmony_ci case IPPROTO_SCTP: 142062306a36Sopenharmony_ci mss_l4len_idx = sizeof(struct sctphdr) << 142162306a36Sopenharmony_ci WX_TXD_L4LEN_SHIFT; 142262306a36Sopenharmony_ci break; 142362306a36Sopenharmony_ci case IPPROTO_UDP: 142462306a36Sopenharmony_ci mss_l4len_idx = sizeof(struct udphdr) << 142562306a36Sopenharmony_ci WX_TXD_L4LEN_SHIFT; 142662306a36Sopenharmony_ci break; 142762306a36Sopenharmony_ci default: 142862306a36Sopenharmony_ci break; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci /* update TX checksum flag */ 143262306a36Sopenharmony_ci first->tx_flags |= WX_TX_FLAGS_CSUM; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci first->tx_flags |= WX_TX_FLAGS_CC; 143562306a36Sopenharmony_ci /* vlan_macip_lens: MACLEN, VLAN tag */ 143662306a36Sopenharmony_ci vlan_macip_lens |= first->tx_flags & WX_TX_FLAGS_VLAN_MASK; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci type_tucmd = ptype << 24; 143962306a36Sopenharmony_ci if (skb->vlan_proto == htons(ETH_P_8021AD) && 144062306a36Sopenharmony_ci netdev->features & NETIF_F_HW_VLAN_STAG_TX) 144162306a36Sopenharmony_ci type_tucmd |= WX_SET_FLAG(first->tx_flags, 144262306a36Sopenharmony_ci WX_TX_FLAGS_HW_VLAN, 144362306a36Sopenharmony_ci 0x1 << WX_TXD_TAG_TPID_SEL_SHIFT); 144462306a36Sopenharmony_ci wx_tx_ctxtdesc(tx_ring, vlan_macip_lens, tunhdr_eiplen_tunlen, 144562306a36Sopenharmony_ci type_tucmd, mss_l4len_idx); 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb, 144962306a36Sopenharmony_ci struct wx_ring *tx_ring) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci u16 count = TXD_USE_COUNT(skb_headlen(skb)); 145262306a36Sopenharmony_ci struct wx_tx_buffer *first; 145362306a36Sopenharmony_ci u8 hdr_len = 0, ptype; 145462306a36Sopenharmony_ci unsigned short f; 145562306a36Sopenharmony_ci u32 tx_flags = 0; 145662306a36Sopenharmony_ci int tso; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* need: 1 descriptor per page * PAGE_SIZE/WX_MAX_DATA_PER_TXD, 145962306a36Sopenharmony_ci * + 1 desc for skb_headlen/WX_MAX_DATA_PER_TXD, 146062306a36Sopenharmony_ci * + 2 desc gap to keep tail from touching head, 146162306a36Sopenharmony_ci * + 1 desc for context descriptor, 146262306a36Sopenharmony_ci * otherwise try next time 146362306a36Sopenharmony_ci */ 146462306a36Sopenharmony_ci for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) 146562306a36Sopenharmony_ci count += TXD_USE_COUNT(skb_frag_size(&skb_shinfo(skb)-> 146662306a36Sopenharmony_ci frags[f])); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (wx_maybe_stop_tx(tx_ring, count + 3)) 146962306a36Sopenharmony_ci return NETDEV_TX_BUSY; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci /* record the location of the first descriptor for this packet */ 147262306a36Sopenharmony_ci first = &tx_ring->tx_buffer_info[tx_ring->next_to_use]; 147362306a36Sopenharmony_ci first->skb = skb; 147462306a36Sopenharmony_ci first->bytecount = skb->len; 147562306a36Sopenharmony_ci first->gso_segs = 1; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci /* if we have a HW VLAN tag being added default to the HW one */ 147862306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 147962306a36Sopenharmony_ci tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT; 148062306a36Sopenharmony_ci tx_flags |= WX_TX_FLAGS_HW_VLAN; 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci /* record initial flags and protocol */ 148462306a36Sopenharmony_ci first->tx_flags = tx_flags; 148562306a36Sopenharmony_ci first->protocol = vlan_get_protocol(skb); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci ptype = wx_encode_tx_desc_ptype(first); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci tso = wx_tso(tx_ring, first, &hdr_len, ptype); 149062306a36Sopenharmony_ci if (tso < 0) 149162306a36Sopenharmony_ci goto out_drop; 149262306a36Sopenharmony_ci else if (!tso) 149362306a36Sopenharmony_ci wx_tx_csum(tx_ring, first, ptype); 149462306a36Sopenharmony_ci wx_tx_map(tx_ring, first, hdr_len); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci return NETDEV_TX_OK; 149762306a36Sopenharmony_ciout_drop: 149862306a36Sopenharmony_ci dev_kfree_skb_any(first->skb); 149962306a36Sopenharmony_ci first->skb = NULL; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci return NETDEV_TX_OK; 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_cinetdev_tx_t wx_xmit_frame(struct sk_buff *skb, 150562306a36Sopenharmony_ci struct net_device *netdev) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci unsigned int r_idx = skb->queue_mapping; 150862306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 150962306a36Sopenharmony_ci struct wx_ring *tx_ring; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci if (!netif_carrier_ok(netdev)) { 151262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 151362306a36Sopenharmony_ci return NETDEV_TX_OK; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci /* The minimum packet size for olinfo paylen is 17 so pad the skb 151762306a36Sopenharmony_ci * in order to meet this minimum size requirement. 151862306a36Sopenharmony_ci */ 151962306a36Sopenharmony_ci if (skb_put_padto(skb, 17)) 152062306a36Sopenharmony_ci return NETDEV_TX_OK; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci if (r_idx >= wx->num_tx_queues) 152362306a36Sopenharmony_ci r_idx = r_idx % wx->num_tx_queues; 152462306a36Sopenharmony_ci tx_ring = wx->tx_ring[r_idx]; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci return wx_xmit_frame_ring(skb, tx_ring); 152762306a36Sopenharmony_ci} 152862306a36Sopenharmony_ciEXPORT_SYMBOL(wx_xmit_frame); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_civoid wx_napi_enable_all(struct wx *wx) 153162306a36Sopenharmony_ci{ 153262306a36Sopenharmony_ci struct wx_q_vector *q_vector; 153362306a36Sopenharmony_ci int q_idx; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci for (q_idx = 0; q_idx < wx->num_q_vectors; q_idx++) { 153662306a36Sopenharmony_ci q_vector = wx->q_vector[q_idx]; 153762306a36Sopenharmony_ci napi_enable(&q_vector->napi); 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci} 154062306a36Sopenharmony_ciEXPORT_SYMBOL(wx_napi_enable_all); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_civoid wx_napi_disable_all(struct wx *wx) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci struct wx_q_vector *q_vector; 154562306a36Sopenharmony_ci int q_idx; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci for (q_idx = 0; q_idx < wx->num_q_vectors; q_idx++) { 154862306a36Sopenharmony_ci q_vector = wx->q_vector[q_idx]; 154962306a36Sopenharmony_ci napi_disable(&q_vector->napi); 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ciEXPORT_SYMBOL(wx_napi_disable_all); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci/** 155562306a36Sopenharmony_ci * wx_set_rss_queues: Allocate queues for RSS 155662306a36Sopenharmony_ci * @wx: board private structure to initialize 155762306a36Sopenharmony_ci * 155862306a36Sopenharmony_ci * This is our "base" multiqueue mode. RSS (Receive Side Scaling) will try 155962306a36Sopenharmony_ci * to allocate one Rx queue per CPU, and if available, one Tx queue per CPU. 156062306a36Sopenharmony_ci * 156162306a36Sopenharmony_ci **/ 156262306a36Sopenharmony_cistatic void wx_set_rss_queues(struct wx *wx) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci wx->num_rx_queues = wx->mac.max_rx_queues; 156562306a36Sopenharmony_ci wx->num_tx_queues = wx->mac.max_tx_queues; 156662306a36Sopenharmony_ci} 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cistatic void wx_set_num_queues(struct wx *wx) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci /* Start with base case */ 157162306a36Sopenharmony_ci wx->num_rx_queues = 1; 157262306a36Sopenharmony_ci wx->num_tx_queues = 1; 157362306a36Sopenharmony_ci wx->queues_per_pool = 1; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci wx_set_rss_queues(wx); 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci/** 157962306a36Sopenharmony_ci * wx_acquire_msix_vectors - acquire MSI-X vectors 158062306a36Sopenharmony_ci * @wx: board private structure 158162306a36Sopenharmony_ci * 158262306a36Sopenharmony_ci * Attempts to acquire a suitable range of MSI-X vector interrupts. Will 158362306a36Sopenharmony_ci * return a negative error code if unable to acquire MSI-X vectors for any 158462306a36Sopenharmony_ci * reason. 158562306a36Sopenharmony_ci */ 158662306a36Sopenharmony_cistatic int wx_acquire_msix_vectors(struct wx *wx) 158762306a36Sopenharmony_ci{ 158862306a36Sopenharmony_ci struct irq_affinity affd = {0, }; 158962306a36Sopenharmony_ci int nvecs, i; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci nvecs = min_t(int, num_online_cpus(), wx->mac.max_msix_vectors); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci wx->msix_entries = kcalloc(nvecs, 159462306a36Sopenharmony_ci sizeof(struct msix_entry), 159562306a36Sopenharmony_ci GFP_KERNEL); 159662306a36Sopenharmony_ci if (!wx->msix_entries) 159762306a36Sopenharmony_ci return -ENOMEM; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci nvecs = pci_alloc_irq_vectors_affinity(wx->pdev, nvecs, 160062306a36Sopenharmony_ci nvecs, 160162306a36Sopenharmony_ci PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, 160262306a36Sopenharmony_ci &affd); 160362306a36Sopenharmony_ci if (nvecs < 0) { 160462306a36Sopenharmony_ci wx_err(wx, "Failed to allocate MSI-X interrupts. Err: %d\n", nvecs); 160562306a36Sopenharmony_ci kfree(wx->msix_entries); 160662306a36Sopenharmony_ci wx->msix_entries = NULL; 160762306a36Sopenharmony_ci return nvecs; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci for (i = 0; i < nvecs; i++) { 161162306a36Sopenharmony_ci wx->msix_entries[i].entry = i; 161262306a36Sopenharmony_ci wx->msix_entries[i].vector = pci_irq_vector(wx->pdev, i); 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* one for msix_other */ 161662306a36Sopenharmony_ci nvecs -= 1; 161762306a36Sopenharmony_ci wx->num_q_vectors = nvecs; 161862306a36Sopenharmony_ci wx->num_rx_queues = nvecs; 161962306a36Sopenharmony_ci wx->num_tx_queues = nvecs; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci return 0; 162262306a36Sopenharmony_ci} 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci/** 162562306a36Sopenharmony_ci * wx_set_interrupt_capability - set MSI-X or MSI if supported 162662306a36Sopenharmony_ci * @wx: board private structure to initialize 162762306a36Sopenharmony_ci * 162862306a36Sopenharmony_ci * Attempt to configure the interrupts using the best available 162962306a36Sopenharmony_ci * capabilities of the hardware and the kernel. 163062306a36Sopenharmony_ci **/ 163162306a36Sopenharmony_cistatic int wx_set_interrupt_capability(struct wx *wx) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 163462306a36Sopenharmony_ci int nvecs, ret; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci /* We will try to get MSI-X interrupts first */ 163762306a36Sopenharmony_ci ret = wx_acquire_msix_vectors(wx); 163862306a36Sopenharmony_ci if (ret == 0 || (ret == -ENOMEM)) 163962306a36Sopenharmony_ci return ret; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci wx->num_rx_queues = 1; 164262306a36Sopenharmony_ci wx->num_tx_queues = 1; 164362306a36Sopenharmony_ci wx->num_q_vectors = 1; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci /* minmum one for queue, one for misc*/ 164662306a36Sopenharmony_ci nvecs = 1; 164762306a36Sopenharmony_ci nvecs = pci_alloc_irq_vectors(pdev, nvecs, 164862306a36Sopenharmony_ci nvecs, PCI_IRQ_MSI | PCI_IRQ_LEGACY); 164962306a36Sopenharmony_ci if (nvecs == 1) { 165062306a36Sopenharmony_ci if (pdev->msi_enabled) 165162306a36Sopenharmony_ci wx_err(wx, "Fallback to MSI.\n"); 165262306a36Sopenharmony_ci else 165362306a36Sopenharmony_ci wx_err(wx, "Fallback to LEGACY.\n"); 165462306a36Sopenharmony_ci } else { 165562306a36Sopenharmony_ci wx_err(wx, "Failed to allocate MSI/LEGACY interrupts. Error: %d\n", nvecs); 165662306a36Sopenharmony_ci return nvecs; 165762306a36Sopenharmony_ci } 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci pdev->irq = pci_irq_vector(pdev, 0); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci return 0; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci/** 166562306a36Sopenharmony_ci * wx_cache_ring_rss - Descriptor ring to register mapping for RSS 166662306a36Sopenharmony_ci * @wx: board private structure to initialize 166762306a36Sopenharmony_ci * 166862306a36Sopenharmony_ci * Cache the descriptor ring offsets for RSS, ATR, FCoE, and SR-IOV. 166962306a36Sopenharmony_ci * 167062306a36Sopenharmony_ci **/ 167162306a36Sopenharmony_cistatic void wx_cache_ring_rss(struct wx *wx) 167262306a36Sopenharmony_ci{ 167362306a36Sopenharmony_ci u16 i; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci for (i = 0; i < wx->num_rx_queues; i++) 167662306a36Sopenharmony_ci wx->rx_ring[i]->reg_idx = i; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci for (i = 0; i < wx->num_tx_queues; i++) 167962306a36Sopenharmony_ci wx->tx_ring[i]->reg_idx = i; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_cistatic void wx_add_ring(struct wx_ring *ring, struct wx_ring_container *head) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci ring->next = head->ring; 168562306a36Sopenharmony_ci head->ring = ring; 168662306a36Sopenharmony_ci head->count++; 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci/** 169062306a36Sopenharmony_ci * wx_alloc_q_vector - Allocate memory for a single interrupt vector 169162306a36Sopenharmony_ci * @wx: board private structure to initialize 169262306a36Sopenharmony_ci * @v_count: q_vectors allocated on wx, used for ring interleaving 169362306a36Sopenharmony_ci * @v_idx: index of vector in wx struct 169462306a36Sopenharmony_ci * @txr_count: total number of Tx rings to allocate 169562306a36Sopenharmony_ci * @txr_idx: index of first Tx ring to allocate 169662306a36Sopenharmony_ci * @rxr_count: total number of Rx rings to allocate 169762306a36Sopenharmony_ci * @rxr_idx: index of first Rx ring to allocate 169862306a36Sopenharmony_ci * 169962306a36Sopenharmony_ci * We allocate one q_vector. If allocation fails we return -ENOMEM. 170062306a36Sopenharmony_ci **/ 170162306a36Sopenharmony_cistatic int wx_alloc_q_vector(struct wx *wx, 170262306a36Sopenharmony_ci unsigned int v_count, unsigned int v_idx, 170362306a36Sopenharmony_ci unsigned int txr_count, unsigned int txr_idx, 170462306a36Sopenharmony_ci unsigned int rxr_count, unsigned int rxr_idx) 170562306a36Sopenharmony_ci{ 170662306a36Sopenharmony_ci struct wx_q_vector *q_vector; 170762306a36Sopenharmony_ci int ring_count, default_itr; 170862306a36Sopenharmony_ci struct wx_ring *ring; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci /* note this will allocate space for the ring structure as well! */ 171162306a36Sopenharmony_ci ring_count = txr_count + rxr_count; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci q_vector = kzalloc(struct_size(q_vector, ring, ring_count), 171462306a36Sopenharmony_ci GFP_KERNEL); 171562306a36Sopenharmony_ci if (!q_vector) 171662306a36Sopenharmony_ci return -ENOMEM; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci /* initialize NAPI */ 171962306a36Sopenharmony_ci netif_napi_add(wx->netdev, &q_vector->napi, 172062306a36Sopenharmony_ci wx_poll); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci /* tie q_vector and wx together */ 172362306a36Sopenharmony_ci wx->q_vector[v_idx] = q_vector; 172462306a36Sopenharmony_ci q_vector->wx = wx; 172562306a36Sopenharmony_ci q_vector->v_idx = v_idx; 172662306a36Sopenharmony_ci if (cpu_online(v_idx)) 172762306a36Sopenharmony_ci q_vector->numa_node = cpu_to_node(v_idx); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci /* initialize pointer to rings */ 173062306a36Sopenharmony_ci ring = q_vector->ring; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci if (wx->mac.type == wx_mac_sp) 173362306a36Sopenharmony_ci default_itr = WX_12K_ITR; 173462306a36Sopenharmony_ci else 173562306a36Sopenharmony_ci default_itr = WX_7K_ITR; 173662306a36Sopenharmony_ci /* initialize ITR */ 173762306a36Sopenharmony_ci if (txr_count && !rxr_count) 173862306a36Sopenharmony_ci /* tx only vector */ 173962306a36Sopenharmony_ci q_vector->itr = wx->tx_itr_setting ? 174062306a36Sopenharmony_ci default_itr : wx->tx_itr_setting; 174162306a36Sopenharmony_ci else 174262306a36Sopenharmony_ci /* rx or rx/tx vector */ 174362306a36Sopenharmony_ci q_vector->itr = wx->rx_itr_setting ? 174462306a36Sopenharmony_ci default_itr : wx->rx_itr_setting; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci while (txr_count) { 174762306a36Sopenharmony_ci /* assign generic ring traits */ 174862306a36Sopenharmony_ci ring->dev = &wx->pdev->dev; 174962306a36Sopenharmony_ci ring->netdev = wx->netdev; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci /* configure backlink on ring */ 175262306a36Sopenharmony_ci ring->q_vector = q_vector; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci /* update q_vector Tx values */ 175562306a36Sopenharmony_ci wx_add_ring(ring, &q_vector->tx); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* apply Tx specific ring traits */ 175862306a36Sopenharmony_ci ring->count = wx->tx_ring_count; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci ring->queue_index = txr_idx; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci /* assign ring to wx */ 176362306a36Sopenharmony_ci wx->tx_ring[txr_idx] = ring; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci /* update count and index */ 176662306a36Sopenharmony_ci txr_count--; 176762306a36Sopenharmony_ci txr_idx += v_count; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci /* push pointer to next ring */ 177062306a36Sopenharmony_ci ring++; 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci while (rxr_count) { 177462306a36Sopenharmony_ci /* assign generic ring traits */ 177562306a36Sopenharmony_ci ring->dev = &wx->pdev->dev; 177662306a36Sopenharmony_ci ring->netdev = wx->netdev; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci /* configure backlink on ring */ 177962306a36Sopenharmony_ci ring->q_vector = q_vector; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci /* update q_vector Rx values */ 178262306a36Sopenharmony_ci wx_add_ring(ring, &q_vector->rx); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci /* apply Rx specific ring traits */ 178562306a36Sopenharmony_ci ring->count = wx->rx_ring_count; 178662306a36Sopenharmony_ci ring->queue_index = rxr_idx; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci /* assign ring to wx */ 178962306a36Sopenharmony_ci wx->rx_ring[rxr_idx] = ring; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci /* update count and index */ 179262306a36Sopenharmony_ci rxr_count--; 179362306a36Sopenharmony_ci rxr_idx += v_count; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci /* push pointer to next ring */ 179662306a36Sopenharmony_ci ring++; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci return 0; 180062306a36Sopenharmony_ci} 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci/** 180362306a36Sopenharmony_ci * wx_free_q_vector - Free memory allocated for specific interrupt vector 180462306a36Sopenharmony_ci * @wx: board private structure to initialize 180562306a36Sopenharmony_ci * @v_idx: Index of vector to be freed 180662306a36Sopenharmony_ci * 180762306a36Sopenharmony_ci * This function frees the memory allocated to the q_vector. In addition if 180862306a36Sopenharmony_ci * NAPI is enabled it will delete any references to the NAPI struct prior 180962306a36Sopenharmony_ci * to freeing the q_vector. 181062306a36Sopenharmony_ci **/ 181162306a36Sopenharmony_cistatic void wx_free_q_vector(struct wx *wx, int v_idx) 181262306a36Sopenharmony_ci{ 181362306a36Sopenharmony_ci struct wx_q_vector *q_vector = wx->q_vector[v_idx]; 181462306a36Sopenharmony_ci struct wx_ring *ring; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci wx_for_each_ring(ring, q_vector->tx) 181762306a36Sopenharmony_ci wx->tx_ring[ring->queue_index] = NULL; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci wx_for_each_ring(ring, q_vector->rx) 182062306a36Sopenharmony_ci wx->rx_ring[ring->queue_index] = NULL; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci wx->q_vector[v_idx] = NULL; 182362306a36Sopenharmony_ci netif_napi_del(&q_vector->napi); 182462306a36Sopenharmony_ci kfree_rcu(q_vector, rcu); 182562306a36Sopenharmony_ci} 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci/** 182862306a36Sopenharmony_ci * wx_alloc_q_vectors - Allocate memory for interrupt vectors 182962306a36Sopenharmony_ci * @wx: board private structure to initialize 183062306a36Sopenharmony_ci * 183162306a36Sopenharmony_ci * We allocate one q_vector per queue interrupt. If allocation fails we 183262306a36Sopenharmony_ci * return -ENOMEM. 183362306a36Sopenharmony_ci **/ 183462306a36Sopenharmony_cistatic int wx_alloc_q_vectors(struct wx *wx) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci unsigned int rxr_idx = 0, txr_idx = 0, v_idx = 0; 183762306a36Sopenharmony_ci unsigned int rxr_remaining = wx->num_rx_queues; 183862306a36Sopenharmony_ci unsigned int txr_remaining = wx->num_tx_queues; 183962306a36Sopenharmony_ci unsigned int q_vectors = wx->num_q_vectors; 184062306a36Sopenharmony_ci int rqpv, tqpv; 184162306a36Sopenharmony_ci int err; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci for (; v_idx < q_vectors; v_idx++) { 184462306a36Sopenharmony_ci rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - v_idx); 184562306a36Sopenharmony_ci tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - v_idx); 184662306a36Sopenharmony_ci err = wx_alloc_q_vector(wx, q_vectors, v_idx, 184762306a36Sopenharmony_ci tqpv, txr_idx, 184862306a36Sopenharmony_ci rqpv, rxr_idx); 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if (err) 185162306a36Sopenharmony_ci goto err_out; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci /* update counts and index */ 185462306a36Sopenharmony_ci rxr_remaining -= rqpv; 185562306a36Sopenharmony_ci txr_remaining -= tqpv; 185662306a36Sopenharmony_ci rxr_idx++; 185762306a36Sopenharmony_ci txr_idx++; 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci return 0; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_cierr_out: 186362306a36Sopenharmony_ci wx->num_tx_queues = 0; 186462306a36Sopenharmony_ci wx->num_rx_queues = 0; 186562306a36Sopenharmony_ci wx->num_q_vectors = 0; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci while (v_idx--) 186862306a36Sopenharmony_ci wx_free_q_vector(wx, v_idx); 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci return -ENOMEM; 187162306a36Sopenharmony_ci} 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci/** 187462306a36Sopenharmony_ci * wx_free_q_vectors - Free memory allocated for interrupt vectors 187562306a36Sopenharmony_ci * @wx: board private structure to initialize 187662306a36Sopenharmony_ci * 187762306a36Sopenharmony_ci * This function frees the memory allocated to the q_vectors. In addition if 187862306a36Sopenharmony_ci * NAPI is enabled it will delete any references to the NAPI struct prior 187962306a36Sopenharmony_ci * to freeing the q_vector. 188062306a36Sopenharmony_ci **/ 188162306a36Sopenharmony_cistatic void wx_free_q_vectors(struct wx *wx) 188262306a36Sopenharmony_ci{ 188362306a36Sopenharmony_ci int v_idx = wx->num_q_vectors; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci wx->num_tx_queues = 0; 188662306a36Sopenharmony_ci wx->num_rx_queues = 0; 188762306a36Sopenharmony_ci wx->num_q_vectors = 0; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci while (v_idx--) 189062306a36Sopenharmony_ci wx_free_q_vector(wx, v_idx); 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_civoid wx_reset_interrupt_capability(struct wx *wx) 189462306a36Sopenharmony_ci{ 189562306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci if (!pdev->msi_enabled && !pdev->msix_enabled) 189862306a36Sopenharmony_ci return; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci if (pdev->msix_enabled) { 190162306a36Sopenharmony_ci kfree(wx->msix_entries); 190262306a36Sopenharmony_ci wx->msix_entries = NULL; 190362306a36Sopenharmony_ci } 190462306a36Sopenharmony_ci pci_free_irq_vectors(wx->pdev); 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ciEXPORT_SYMBOL(wx_reset_interrupt_capability); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci/** 190962306a36Sopenharmony_ci * wx_clear_interrupt_scheme - Clear the current interrupt scheme settings 191062306a36Sopenharmony_ci * @wx: board private structure to clear interrupt scheme on 191162306a36Sopenharmony_ci * 191262306a36Sopenharmony_ci * We go through and clear interrupt specific resources and reset the structure 191362306a36Sopenharmony_ci * to pre-load conditions 191462306a36Sopenharmony_ci **/ 191562306a36Sopenharmony_civoid wx_clear_interrupt_scheme(struct wx *wx) 191662306a36Sopenharmony_ci{ 191762306a36Sopenharmony_ci wx_free_q_vectors(wx); 191862306a36Sopenharmony_ci wx_reset_interrupt_capability(wx); 191962306a36Sopenharmony_ci} 192062306a36Sopenharmony_ciEXPORT_SYMBOL(wx_clear_interrupt_scheme); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ciint wx_init_interrupt_scheme(struct wx *wx) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci int ret; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* Number of supported queues */ 192762306a36Sopenharmony_ci wx_set_num_queues(wx); 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci /* Set interrupt mode */ 193062306a36Sopenharmony_ci ret = wx_set_interrupt_capability(wx); 193162306a36Sopenharmony_ci if (ret) { 193262306a36Sopenharmony_ci wx_err(wx, "Allocate irq vectors for failed.\n"); 193362306a36Sopenharmony_ci return ret; 193462306a36Sopenharmony_ci } 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci /* Allocate memory for queues */ 193762306a36Sopenharmony_ci ret = wx_alloc_q_vectors(wx); 193862306a36Sopenharmony_ci if (ret) { 193962306a36Sopenharmony_ci wx_err(wx, "Unable to allocate memory for queue vectors.\n"); 194062306a36Sopenharmony_ci wx_reset_interrupt_capability(wx); 194162306a36Sopenharmony_ci return ret; 194262306a36Sopenharmony_ci } 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci wx_cache_ring_rss(wx); 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci return 0; 194762306a36Sopenharmony_ci} 194862306a36Sopenharmony_ciEXPORT_SYMBOL(wx_init_interrupt_scheme); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ciirqreturn_t wx_msix_clean_rings(int __always_unused irq, void *data) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci struct wx_q_vector *q_vector = data; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci /* EIAM disabled interrupts (on this vector) for us */ 195562306a36Sopenharmony_ci if (q_vector->rx.ring || q_vector->tx.ring) 195662306a36Sopenharmony_ci napi_schedule_irqoff(&q_vector->napi); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci return IRQ_HANDLED; 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ciEXPORT_SYMBOL(wx_msix_clean_rings); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_civoid wx_free_irq(struct wx *wx) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 196562306a36Sopenharmony_ci int vector; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci if (!(pdev->msix_enabled)) { 196862306a36Sopenharmony_ci free_irq(pdev->irq, wx); 196962306a36Sopenharmony_ci return; 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci for (vector = 0; vector < wx->num_q_vectors; vector++) { 197362306a36Sopenharmony_ci struct wx_q_vector *q_vector = wx->q_vector[vector]; 197462306a36Sopenharmony_ci struct msix_entry *entry = &wx->msix_entries[vector]; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci /* free only the irqs that were actually requested */ 197762306a36Sopenharmony_ci if (!q_vector->rx.ring && !q_vector->tx.ring) 197862306a36Sopenharmony_ci continue; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci free_irq(entry->vector, q_vector); 198162306a36Sopenharmony_ci } 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci if (wx->mac.type == wx_mac_em) 198462306a36Sopenharmony_ci free_irq(wx->msix_entries[vector].vector, wx); 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ciEXPORT_SYMBOL(wx_free_irq); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci/** 198962306a36Sopenharmony_ci * wx_setup_isb_resources - allocate interrupt status resources 199062306a36Sopenharmony_ci * @wx: board private structure 199162306a36Sopenharmony_ci * 199262306a36Sopenharmony_ci * Return 0 on success, negative on failure 199362306a36Sopenharmony_ci **/ 199462306a36Sopenharmony_ciint wx_setup_isb_resources(struct wx *wx) 199562306a36Sopenharmony_ci{ 199662306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci wx->isb_mem = dma_alloc_coherent(&pdev->dev, 199962306a36Sopenharmony_ci sizeof(u32) * 4, 200062306a36Sopenharmony_ci &wx->isb_dma, 200162306a36Sopenharmony_ci GFP_KERNEL); 200262306a36Sopenharmony_ci if (!wx->isb_mem) { 200362306a36Sopenharmony_ci wx_err(wx, "Alloc isb_mem failed\n"); 200462306a36Sopenharmony_ci return -ENOMEM; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci return 0; 200862306a36Sopenharmony_ci} 200962306a36Sopenharmony_ciEXPORT_SYMBOL(wx_setup_isb_resources); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci/** 201262306a36Sopenharmony_ci * wx_free_isb_resources - allocate all queues Rx resources 201362306a36Sopenharmony_ci * @wx: board private structure 201462306a36Sopenharmony_ci * 201562306a36Sopenharmony_ci * Return 0 on success, negative on failure 201662306a36Sopenharmony_ci **/ 201762306a36Sopenharmony_civoid wx_free_isb_resources(struct wx *wx) 201862306a36Sopenharmony_ci{ 201962306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(u32) * 4, 202262306a36Sopenharmony_ci wx->isb_mem, wx->isb_dma); 202362306a36Sopenharmony_ci wx->isb_mem = NULL; 202462306a36Sopenharmony_ci} 202562306a36Sopenharmony_ciEXPORT_SYMBOL(wx_free_isb_resources); 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ciu32 wx_misc_isb(struct wx *wx, enum wx_isb_idx idx) 202862306a36Sopenharmony_ci{ 202962306a36Sopenharmony_ci u32 cur_tag = 0; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci cur_tag = wx->isb_mem[WX_ISB_HEADER]; 203262306a36Sopenharmony_ci wx->isb_tag[idx] = cur_tag; 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci return (__force u32)cpu_to_le32(wx->isb_mem[idx]); 203562306a36Sopenharmony_ci} 203662306a36Sopenharmony_ciEXPORT_SYMBOL(wx_misc_isb); 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci/** 203962306a36Sopenharmony_ci * wx_set_ivar - set the IVAR registers, mapping interrupt causes to vectors 204062306a36Sopenharmony_ci * @wx: pointer to wx struct 204162306a36Sopenharmony_ci * @direction: 0 for Rx, 1 for Tx, -1 for other causes 204262306a36Sopenharmony_ci * @queue: queue to map the corresponding interrupt to 204362306a36Sopenharmony_ci * @msix_vector: the vector to map to the corresponding queue 204462306a36Sopenharmony_ci * 204562306a36Sopenharmony_ci **/ 204662306a36Sopenharmony_cistatic void wx_set_ivar(struct wx *wx, s8 direction, 204762306a36Sopenharmony_ci u16 queue, u16 msix_vector) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci u32 ivar, index; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci if (direction == -1) { 205262306a36Sopenharmony_ci /* other causes */ 205362306a36Sopenharmony_ci msix_vector |= WX_PX_IVAR_ALLOC_VAL; 205462306a36Sopenharmony_ci index = 0; 205562306a36Sopenharmony_ci ivar = rd32(wx, WX_PX_MISC_IVAR); 205662306a36Sopenharmony_ci ivar &= ~(0xFF << index); 205762306a36Sopenharmony_ci ivar |= (msix_vector << index); 205862306a36Sopenharmony_ci wr32(wx, WX_PX_MISC_IVAR, ivar); 205962306a36Sopenharmony_ci } else { 206062306a36Sopenharmony_ci /* tx or rx causes */ 206162306a36Sopenharmony_ci msix_vector |= WX_PX_IVAR_ALLOC_VAL; 206262306a36Sopenharmony_ci index = ((16 * (queue & 1)) + (8 * direction)); 206362306a36Sopenharmony_ci ivar = rd32(wx, WX_PX_IVAR(queue >> 1)); 206462306a36Sopenharmony_ci ivar &= ~(0xFF << index); 206562306a36Sopenharmony_ci ivar |= (msix_vector << index); 206662306a36Sopenharmony_ci wr32(wx, WX_PX_IVAR(queue >> 1), ivar); 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci} 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci/** 207162306a36Sopenharmony_ci * wx_write_eitr - write EITR register in hardware specific way 207262306a36Sopenharmony_ci * @q_vector: structure containing interrupt and ring information 207362306a36Sopenharmony_ci * 207462306a36Sopenharmony_ci * This function is made to be called by ethtool and by the driver 207562306a36Sopenharmony_ci * when it needs to update EITR registers at runtime. Hardware 207662306a36Sopenharmony_ci * specific quirks/differences are taken care of here. 207762306a36Sopenharmony_ci */ 207862306a36Sopenharmony_cistatic void wx_write_eitr(struct wx_q_vector *q_vector) 207962306a36Sopenharmony_ci{ 208062306a36Sopenharmony_ci struct wx *wx = q_vector->wx; 208162306a36Sopenharmony_ci int v_idx = q_vector->v_idx; 208262306a36Sopenharmony_ci u32 itr_reg; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (wx->mac.type == wx_mac_sp) 208562306a36Sopenharmony_ci itr_reg = q_vector->itr & WX_SP_MAX_EITR; 208662306a36Sopenharmony_ci else 208762306a36Sopenharmony_ci itr_reg = q_vector->itr & WX_EM_MAX_EITR; 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci itr_reg |= WX_PX_ITR_CNT_WDIS; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci wr32(wx, WX_PX_ITR(v_idx), itr_reg); 209262306a36Sopenharmony_ci} 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci/** 209562306a36Sopenharmony_ci * wx_configure_vectors - Configure vectors for hardware 209662306a36Sopenharmony_ci * @wx: board private structure 209762306a36Sopenharmony_ci * 209862306a36Sopenharmony_ci * wx_configure_vectors sets up the hardware to properly generate MSI-X/MSI/LEGACY 209962306a36Sopenharmony_ci * interrupts. 210062306a36Sopenharmony_ci **/ 210162306a36Sopenharmony_civoid wx_configure_vectors(struct wx *wx) 210262306a36Sopenharmony_ci{ 210362306a36Sopenharmony_ci struct pci_dev *pdev = wx->pdev; 210462306a36Sopenharmony_ci u32 eitrsel = 0; 210562306a36Sopenharmony_ci u16 v_idx; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci if (pdev->msix_enabled) { 210862306a36Sopenharmony_ci /* Populate MSIX to EITR Select */ 210962306a36Sopenharmony_ci wr32(wx, WX_PX_ITRSEL, eitrsel); 211062306a36Sopenharmony_ci /* use EIAM to auto-mask when MSI-X interrupt is asserted 211162306a36Sopenharmony_ci * this saves a register write for every interrupt 211262306a36Sopenharmony_ci */ 211362306a36Sopenharmony_ci wr32(wx, WX_PX_GPIE, WX_PX_GPIE_MODEL); 211462306a36Sopenharmony_ci } else { 211562306a36Sopenharmony_ci /* legacy interrupts, use EIAM to auto-mask when reading EICR, 211662306a36Sopenharmony_ci * specifically only auto mask tx and rx interrupts. 211762306a36Sopenharmony_ci */ 211862306a36Sopenharmony_ci wr32(wx, WX_PX_GPIE, 0); 211962306a36Sopenharmony_ci } 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci /* Populate the IVAR table and set the ITR values to the 212262306a36Sopenharmony_ci * corresponding register. 212362306a36Sopenharmony_ci */ 212462306a36Sopenharmony_ci for (v_idx = 0; v_idx < wx->num_q_vectors; v_idx++) { 212562306a36Sopenharmony_ci struct wx_q_vector *q_vector = wx->q_vector[v_idx]; 212662306a36Sopenharmony_ci struct wx_ring *ring; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci wx_for_each_ring(ring, q_vector->rx) 212962306a36Sopenharmony_ci wx_set_ivar(wx, 0, ring->reg_idx, v_idx); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci wx_for_each_ring(ring, q_vector->tx) 213262306a36Sopenharmony_ci wx_set_ivar(wx, 1, ring->reg_idx, v_idx); 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci wx_write_eitr(q_vector); 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci wx_set_ivar(wx, -1, 0, v_idx); 213862306a36Sopenharmony_ci if (pdev->msix_enabled) 213962306a36Sopenharmony_ci wr32(wx, WX_PX_ITR(v_idx), 1950); 214062306a36Sopenharmony_ci} 214162306a36Sopenharmony_ciEXPORT_SYMBOL(wx_configure_vectors); 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci/** 214462306a36Sopenharmony_ci * wx_clean_rx_ring - Free Rx Buffers per Queue 214562306a36Sopenharmony_ci * @rx_ring: ring to free buffers from 214662306a36Sopenharmony_ci **/ 214762306a36Sopenharmony_cistatic void wx_clean_rx_ring(struct wx_ring *rx_ring) 214862306a36Sopenharmony_ci{ 214962306a36Sopenharmony_ci struct wx_rx_buffer *rx_buffer; 215062306a36Sopenharmony_ci u16 i = rx_ring->next_to_clean; 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci rx_buffer = &rx_ring->rx_buffer_info[i]; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci /* Free all the Rx ring sk_buffs */ 215562306a36Sopenharmony_ci while (i != rx_ring->next_to_alloc) { 215662306a36Sopenharmony_ci if (rx_buffer->skb) { 215762306a36Sopenharmony_ci struct sk_buff *skb = rx_buffer->skb; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci if (WX_CB(skb)->page_released) 216062306a36Sopenharmony_ci page_pool_put_full_page(rx_ring->page_pool, rx_buffer->page, false); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci dev_kfree_skb(skb); 216362306a36Sopenharmony_ci } 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci /* Invalidate cache lines that may have been written to by 216662306a36Sopenharmony_ci * device so that we avoid corrupting memory. 216762306a36Sopenharmony_ci */ 216862306a36Sopenharmony_ci dma_sync_single_range_for_cpu(rx_ring->dev, 216962306a36Sopenharmony_ci rx_buffer->dma, 217062306a36Sopenharmony_ci rx_buffer->page_offset, 217162306a36Sopenharmony_ci WX_RX_BUFSZ, 217262306a36Sopenharmony_ci DMA_FROM_DEVICE); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci /* free resources associated with mapping */ 217562306a36Sopenharmony_ci page_pool_put_full_page(rx_ring->page_pool, rx_buffer->page, false); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci i++; 217862306a36Sopenharmony_ci rx_buffer++; 217962306a36Sopenharmony_ci if (i == rx_ring->count) { 218062306a36Sopenharmony_ci i = 0; 218162306a36Sopenharmony_ci rx_buffer = rx_ring->rx_buffer_info; 218262306a36Sopenharmony_ci } 218362306a36Sopenharmony_ci } 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci rx_ring->next_to_alloc = 0; 218662306a36Sopenharmony_ci rx_ring->next_to_clean = 0; 218762306a36Sopenharmony_ci rx_ring->next_to_use = 0; 218862306a36Sopenharmony_ci} 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci/** 219162306a36Sopenharmony_ci * wx_clean_all_rx_rings - Free Rx Buffers for all queues 219262306a36Sopenharmony_ci * @wx: board private structure 219362306a36Sopenharmony_ci **/ 219462306a36Sopenharmony_civoid wx_clean_all_rx_rings(struct wx *wx) 219562306a36Sopenharmony_ci{ 219662306a36Sopenharmony_ci int i; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci for (i = 0; i < wx->num_rx_queues; i++) 219962306a36Sopenharmony_ci wx_clean_rx_ring(wx->rx_ring[i]); 220062306a36Sopenharmony_ci} 220162306a36Sopenharmony_ciEXPORT_SYMBOL(wx_clean_all_rx_rings); 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci/** 220462306a36Sopenharmony_ci * wx_free_rx_resources - Free Rx Resources 220562306a36Sopenharmony_ci * @rx_ring: ring to clean the resources from 220662306a36Sopenharmony_ci * 220762306a36Sopenharmony_ci * Free all receive software resources 220862306a36Sopenharmony_ci **/ 220962306a36Sopenharmony_cistatic void wx_free_rx_resources(struct wx_ring *rx_ring) 221062306a36Sopenharmony_ci{ 221162306a36Sopenharmony_ci wx_clean_rx_ring(rx_ring); 221262306a36Sopenharmony_ci kvfree(rx_ring->rx_buffer_info); 221362306a36Sopenharmony_ci rx_ring->rx_buffer_info = NULL; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci /* if not set, then don't free */ 221662306a36Sopenharmony_ci if (!rx_ring->desc) 221762306a36Sopenharmony_ci return; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci dma_free_coherent(rx_ring->dev, rx_ring->size, 222062306a36Sopenharmony_ci rx_ring->desc, rx_ring->dma); 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci rx_ring->desc = NULL; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci if (rx_ring->page_pool) { 222562306a36Sopenharmony_ci page_pool_destroy(rx_ring->page_pool); 222662306a36Sopenharmony_ci rx_ring->page_pool = NULL; 222762306a36Sopenharmony_ci } 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci/** 223162306a36Sopenharmony_ci * wx_free_all_rx_resources - Free Rx Resources for All Queues 223262306a36Sopenharmony_ci * @wx: pointer to hardware structure 223362306a36Sopenharmony_ci * 223462306a36Sopenharmony_ci * Free all receive software resources 223562306a36Sopenharmony_ci **/ 223662306a36Sopenharmony_cistatic void wx_free_all_rx_resources(struct wx *wx) 223762306a36Sopenharmony_ci{ 223862306a36Sopenharmony_ci int i; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci for (i = 0; i < wx->num_rx_queues; i++) 224162306a36Sopenharmony_ci wx_free_rx_resources(wx->rx_ring[i]); 224262306a36Sopenharmony_ci} 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci/** 224562306a36Sopenharmony_ci * wx_clean_tx_ring - Free Tx Buffers 224662306a36Sopenharmony_ci * @tx_ring: ring to be cleaned 224762306a36Sopenharmony_ci **/ 224862306a36Sopenharmony_cistatic void wx_clean_tx_ring(struct wx_ring *tx_ring) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct wx_tx_buffer *tx_buffer; 225162306a36Sopenharmony_ci u16 i = tx_ring->next_to_clean; 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci tx_buffer = &tx_ring->tx_buffer_info[i]; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci while (i != tx_ring->next_to_use) { 225662306a36Sopenharmony_ci union wx_tx_desc *eop_desc, *tx_desc; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci /* Free all the Tx ring sk_buffs */ 225962306a36Sopenharmony_ci dev_kfree_skb_any(tx_buffer->skb); 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci /* unmap skb header data */ 226262306a36Sopenharmony_ci dma_unmap_single(tx_ring->dev, 226362306a36Sopenharmony_ci dma_unmap_addr(tx_buffer, dma), 226462306a36Sopenharmony_ci dma_unmap_len(tx_buffer, len), 226562306a36Sopenharmony_ci DMA_TO_DEVICE); 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci /* check for eop_desc to determine the end of the packet */ 226862306a36Sopenharmony_ci eop_desc = tx_buffer->next_to_watch; 226962306a36Sopenharmony_ci tx_desc = WX_TX_DESC(tx_ring, i); 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci /* unmap remaining buffers */ 227262306a36Sopenharmony_ci while (tx_desc != eop_desc) { 227362306a36Sopenharmony_ci tx_buffer++; 227462306a36Sopenharmony_ci tx_desc++; 227562306a36Sopenharmony_ci i++; 227662306a36Sopenharmony_ci if (unlikely(i == tx_ring->count)) { 227762306a36Sopenharmony_ci i = 0; 227862306a36Sopenharmony_ci tx_buffer = tx_ring->tx_buffer_info; 227962306a36Sopenharmony_ci tx_desc = WX_TX_DESC(tx_ring, 0); 228062306a36Sopenharmony_ci } 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci /* unmap any remaining paged data */ 228362306a36Sopenharmony_ci if (dma_unmap_len(tx_buffer, len)) 228462306a36Sopenharmony_ci dma_unmap_page(tx_ring->dev, 228562306a36Sopenharmony_ci dma_unmap_addr(tx_buffer, dma), 228662306a36Sopenharmony_ci dma_unmap_len(tx_buffer, len), 228762306a36Sopenharmony_ci DMA_TO_DEVICE); 228862306a36Sopenharmony_ci } 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci /* move us one more past the eop_desc for start of next pkt */ 229162306a36Sopenharmony_ci tx_buffer++; 229262306a36Sopenharmony_ci i++; 229362306a36Sopenharmony_ci if (unlikely(i == tx_ring->count)) { 229462306a36Sopenharmony_ci i = 0; 229562306a36Sopenharmony_ci tx_buffer = tx_ring->tx_buffer_info; 229662306a36Sopenharmony_ci } 229762306a36Sopenharmony_ci } 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci netdev_tx_reset_queue(wx_txring_txq(tx_ring)); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* reset next_to_use and next_to_clean */ 230262306a36Sopenharmony_ci tx_ring->next_to_use = 0; 230362306a36Sopenharmony_ci tx_ring->next_to_clean = 0; 230462306a36Sopenharmony_ci} 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci/** 230762306a36Sopenharmony_ci * wx_clean_all_tx_rings - Free Tx Buffers for all queues 230862306a36Sopenharmony_ci * @wx: board private structure 230962306a36Sopenharmony_ci **/ 231062306a36Sopenharmony_civoid wx_clean_all_tx_rings(struct wx *wx) 231162306a36Sopenharmony_ci{ 231262306a36Sopenharmony_ci int i; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci for (i = 0; i < wx->num_tx_queues; i++) 231562306a36Sopenharmony_ci wx_clean_tx_ring(wx->tx_ring[i]); 231662306a36Sopenharmony_ci} 231762306a36Sopenharmony_ciEXPORT_SYMBOL(wx_clean_all_tx_rings); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci/** 232062306a36Sopenharmony_ci * wx_free_tx_resources - Free Tx Resources per Queue 232162306a36Sopenharmony_ci * @tx_ring: Tx descriptor ring for a specific queue 232262306a36Sopenharmony_ci * 232362306a36Sopenharmony_ci * Free all transmit software resources 232462306a36Sopenharmony_ci **/ 232562306a36Sopenharmony_cistatic void wx_free_tx_resources(struct wx_ring *tx_ring) 232662306a36Sopenharmony_ci{ 232762306a36Sopenharmony_ci wx_clean_tx_ring(tx_ring); 232862306a36Sopenharmony_ci kvfree(tx_ring->tx_buffer_info); 232962306a36Sopenharmony_ci tx_ring->tx_buffer_info = NULL; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci /* if not set, then don't free */ 233262306a36Sopenharmony_ci if (!tx_ring->desc) 233362306a36Sopenharmony_ci return; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci dma_free_coherent(tx_ring->dev, tx_ring->size, 233662306a36Sopenharmony_ci tx_ring->desc, tx_ring->dma); 233762306a36Sopenharmony_ci tx_ring->desc = NULL; 233862306a36Sopenharmony_ci} 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_ci/** 234162306a36Sopenharmony_ci * wx_free_all_tx_resources - Free Tx Resources for All Queues 234262306a36Sopenharmony_ci * @wx: pointer to hardware structure 234362306a36Sopenharmony_ci * 234462306a36Sopenharmony_ci * Free all transmit software resources 234562306a36Sopenharmony_ci **/ 234662306a36Sopenharmony_cistatic void wx_free_all_tx_resources(struct wx *wx) 234762306a36Sopenharmony_ci{ 234862306a36Sopenharmony_ci int i; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci for (i = 0; i < wx->num_tx_queues; i++) 235162306a36Sopenharmony_ci wx_free_tx_resources(wx->tx_ring[i]); 235262306a36Sopenharmony_ci} 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_civoid wx_free_resources(struct wx *wx) 235562306a36Sopenharmony_ci{ 235662306a36Sopenharmony_ci wx_free_isb_resources(wx); 235762306a36Sopenharmony_ci wx_free_all_rx_resources(wx); 235862306a36Sopenharmony_ci wx_free_all_tx_resources(wx); 235962306a36Sopenharmony_ci} 236062306a36Sopenharmony_ciEXPORT_SYMBOL(wx_free_resources); 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_cistatic int wx_alloc_page_pool(struct wx_ring *rx_ring) 236362306a36Sopenharmony_ci{ 236462306a36Sopenharmony_ci int ret = 0; 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci struct page_pool_params pp_params = { 236762306a36Sopenharmony_ci .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, 236862306a36Sopenharmony_ci .order = 0, 236962306a36Sopenharmony_ci .pool_size = rx_ring->size, 237062306a36Sopenharmony_ci .nid = dev_to_node(rx_ring->dev), 237162306a36Sopenharmony_ci .dev = rx_ring->dev, 237262306a36Sopenharmony_ci .dma_dir = DMA_FROM_DEVICE, 237362306a36Sopenharmony_ci .offset = 0, 237462306a36Sopenharmony_ci .max_len = PAGE_SIZE, 237562306a36Sopenharmony_ci }; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci rx_ring->page_pool = page_pool_create(&pp_params); 237862306a36Sopenharmony_ci if (IS_ERR(rx_ring->page_pool)) { 237962306a36Sopenharmony_ci ret = PTR_ERR(rx_ring->page_pool); 238062306a36Sopenharmony_ci rx_ring->page_pool = NULL; 238162306a36Sopenharmony_ci } 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci return ret; 238462306a36Sopenharmony_ci} 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci/** 238762306a36Sopenharmony_ci * wx_setup_rx_resources - allocate Rx resources (Descriptors) 238862306a36Sopenharmony_ci * @rx_ring: rx descriptor ring (for a specific queue) to setup 238962306a36Sopenharmony_ci * 239062306a36Sopenharmony_ci * Returns 0 on success, negative on failure 239162306a36Sopenharmony_ci **/ 239262306a36Sopenharmony_cistatic int wx_setup_rx_resources(struct wx_ring *rx_ring) 239362306a36Sopenharmony_ci{ 239462306a36Sopenharmony_ci struct device *dev = rx_ring->dev; 239562306a36Sopenharmony_ci int orig_node = dev_to_node(dev); 239662306a36Sopenharmony_ci int numa_node = NUMA_NO_NODE; 239762306a36Sopenharmony_ci int size, ret; 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci size = sizeof(struct wx_rx_buffer) * rx_ring->count; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci if (rx_ring->q_vector) 240262306a36Sopenharmony_ci numa_node = rx_ring->q_vector->numa_node; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci rx_ring->rx_buffer_info = kvmalloc_node(size, GFP_KERNEL, numa_node); 240562306a36Sopenharmony_ci if (!rx_ring->rx_buffer_info) 240662306a36Sopenharmony_ci rx_ring->rx_buffer_info = kvmalloc(size, GFP_KERNEL); 240762306a36Sopenharmony_ci if (!rx_ring->rx_buffer_info) 240862306a36Sopenharmony_ci goto err; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci /* Round up to nearest 4K */ 241162306a36Sopenharmony_ci rx_ring->size = rx_ring->count * sizeof(union wx_rx_desc); 241262306a36Sopenharmony_ci rx_ring->size = ALIGN(rx_ring->size, 4096); 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci set_dev_node(dev, numa_node); 241562306a36Sopenharmony_ci rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, 241662306a36Sopenharmony_ci &rx_ring->dma, GFP_KERNEL); 241762306a36Sopenharmony_ci if (!rx_ring->desc) { 241862306a36Sopenharmony_ci set_dev_node(dev, orig_node); 241962306a36Sopenharmony_ci rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, 242062306a36Sopenharmony_ci &rx_ring->dma, GFP_KERNEL); 242162306a36Sopenharmony_ci } 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci if (!rx_ring->desc) 242462306a36Sopenharmony_ci goto err; 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci rx_ring->next_to_clean = 0; 242762306a36Sopenharmony_ci rx_ring->next_to_use = 0; 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci ret = wx_alloc_page_pool(rx_ring); 243062306a36Sopenharmony_ci if (ret < 0) { 243162306a36Sopenharmony_ci dev_err(rx_ring->dev, "Page pool creation failed: %d\n", ret); 243262306a36Sopenharmony_ci goto err_desc; 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci return 0; 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_cierr_desc: 243862306a36Sopenharmony_ci dma_free_coherent(dev, rx_ring->size, rx_ring->desc, rx_ring->dma); 243962306a36Sopenharmony_cierr: 244062306a36Sopenharmony_ci kvfree(rx_ring->rx_buffer_info); 244162306a36Sopenharmony_ci rx_ring->rx_buffer_info = NULL; 244262306a36Sopenharmony_ci dev_err(dev, "Unable to allocate memory for the Rx descriptor ring\n"); 244362306a36Sopenharmony_ci return -ENOMEM; 244462306a36Sopenharmony_ci} 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci/** 244762306a36Sopenharmony_ci * wx_setup_all_rx_resources - allocate all queues Rx resources 244862306a36Sopenharmony_ci * @wx: pointer to hardware structure 244962306a36Sopenharmony_ci * 245062306a36Sopenharmony_ci * If this function returns with an error, then it's possible one or 245162306a36Sopenharmony_ci * more of the rings is populated (while the rest are not). It is the 245262306a36Sopenharmony_ci * callers duty to clean those orphaned rings. 245362306a36Sopenharmony_ci * 245462306a36Sopenharmony_ci * Return 0 on success, negative on failure 245562306a36Sopenharmony_ci **/ 245662306a36Sopenharmony_cistatic int wx_setup_all_rx_resources(struct wx *wx) 245762306a36Sopenharmony_ci{ 245862306a36Sopenharmony_ci int i, err = 0; 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci for (i = 0; i < wx->num_rx_queues; i++) { 246162306a36Sopenharmony_ci err = wx_setup_rx_resources(wx->rx_ring[i]); 246262306a36Sopenharmony_ci if (!err) 246362306a36Sopenharmony_ci continue; 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci wx_err(wx, "Allocation for Rx Queue %u failed\n", i); 246662306a36Sopenharmony_ci goto err_setup_rx; 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci return 0; 247062306a36Sopenharmony_cierr_setup_rx: 247162306a36Sopenharmony_ci /* rewind the index freeing the rings as we go */ 247262306a36Sopenharmony_ci while (i--) 247362306a36Sopenharmony_ci wx_free_rx_resources(wx->rx_ring[i]); 247462306a36Sopenharmony_ci return err; 247562306a36Sopenharmony_ci} 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci/** 247862306a36Sopenharmony_ci * wx_setup_tx_resources - allocate Tx resources (Descriptors) 247962306a36Sopenharmony_ci * @tx_ring: tx descriptor ring (for a specific queue) to setup 248062306a36Sopenharmony_ci * 248162306a36Sopenharmony_ci * Return 0 on success, negative on failure 248262306a36Sopenharmony_ci **/ 248362306a36Sopenharmony_cistatic int wx_setup_tx_resources(struct wx_ring *tx_ring) 248462306a36Sopenharmony_ci{ 248562306a36Sopenharmony_ci struct device *dev = tx_ring->dev; 248662306a36Sopenharmony_ci int orig_node = dev_to_node(dev); 248762306a36Sopenharmony_ci int numa_node = NUMA_NO_NODE; 248862306a36Sopenharmony_ci int size; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci size = sizeof(struct wx_tx_buffer) * tx_ring->count; 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci if (tx_ring->q_vector) 249362306a36Sopenharmony_ci numa_node = tx_ring->q_vector->numa_node; 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci tx_ring->tx_buffer_info = kvmalloc_node(size, GFP_KERNEL, numa_node); 249662306a36Sopenharmony_ci if (!tx_ring->tx_buffer_info) 249762306a36Sopenharmony_ci tx_ring->tx_buffer_info = kvmalloc(size, GFP_KERNEL); 249862306a36Sopenharmony_ci if (!tx_ring->tx_buffer_info) 249962306a36Sopenharmony_ci goto err; 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci /* round up to nearest 4K */ 250262306a36Sopenharmony_ci tx_ring->size = tx_ring->count * sizeof(union wx_tx_desc); 250362306a36Sopenharmony_ci tx_ring->size = ALIGN(tx_ring->size, 4096); 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci set_dev_node(dev, numa_node); 250662306a36Sopenharmony_ci tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size, 250762306a36Sopenharmony_ci &tx_ring->dma, GFP_KERNEL); 250862306a36Sopenharmony_ci if (!tx_ring->desc) { 250962306a36Sopenharmony_ci set_dev_node(dev, orig_node); 251062306a36Sopenharmony_ci tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size, 251162306a36Sopenharmony_ci &tx_ring->dma, GFP_KERNEL); 251262306a36Sopenharmony_ci } 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci if (!tx_ring->desc) 251562306a36Sopenharmony_ci goto err; 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci tx_ring->next_to_use = 0; 251862306a36Sopenharmony_ci tx_ring->next_to_clean = 0; 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci return 0; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_cierr: 252362306a36Sopenharmony_ci kvfree(tx_ring->tx_buffer_info); 252462306a36Sopenharmony_ci tx_ring->tx_buffer_info = NULL; 252562306a36Sopenharmony_ci dev_err(dev, "Unable to allocate memory for the Tx descriptor ring\n"); 252662306a36Sopenharmony_ci return -ENOMEM; 252762306a36Sopenharmony_ci} 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci/** 253062306a36Sopenharmony_ci * wx_setup_all_tx_resources - allocate all queues Tx resources 253162306a36Sopenharmony_ci * @wx: pointer to private structure 253262306a36Sopenharmony_ci * 253362306a36Sopenharmony_ci * If this function returns with an error, then it's possible one or 253462306a36Sopenharmony_ci * more of the rings is populated (while the rest are not). It is the 253562306a36Sopenharmony_ci * callers duty to clean those orphaned rings. 253662306a36Sopenharmony_ci * 253762306a36Sopenharmony_ci * Return 0 on success, negative on failure 253862306a36Sopenharmony_ci **/ 253962306a36Sopenharmony_cistatic int wx_setup_all_tx_resources(struct wx *wx) 254062306a36Sopenharmony_ci{ 254162306a36Sopenharmony_ci int i, err = 0; 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci for (i = 0; i < wx->num_tx_queues; i++) { 254462306a36Sopenharmony_ci err = wx_setup_tx_resources(wx->tx_ring[i]); 254562306a36Sopenharmony_ci if (!err) 254662306a36Sopenharmony_ci continue; 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci wx_err(wx, "Allocation for Tx Queue %u failed\n", i); 254962306a36Sopenharmony_ci goto err_setup_tx; 255062306a36Sopenharmony_ci } 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci return 0; 255362306a36Sopenharmony_cierr_setup_tx: 255462306a36Sopenharmony_ci /* rewind the index freeing the rings as we go */ 255562306a36Sopenharmony_ci while (i--) 255662306a36Sopenharmony_ci wx_free_tx_resources(wx->tx_ring[i]); 255762306a36Sopenharmony_ci return err; 255862306a36Sopenharmony_ci} 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ciint wx_setup_resources(struct wx *wx) 256162306a36Sopenharmony_ci{ 256262306a36Sopenharmony_ci int err; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci /* allocate transmit descriptors */ 256562306a36Sopenharmony_ci err = wx_setup_all_tx_resources(wx); 256662306a36Sopenharmony_ci if (err) 256762306a36Sopenharmony_ci return err; 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci /* allocate receive descriptors */ 257062306a36Sopenharmony_ci err = wx_setup_all_rx_resources(wx); 257162306a36Sopenharmony_ci if (err) 257262306a36Sopenharmony_ci goto err_free_tx; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci err = wx_setup_isb_resources(wx); 257562306a36Sopenharmony_ci if (err) 257662306a36Sopenharmony_ci goto err_free_rx; 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci return 0; 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_cierr_free_rx: 258162306a36Sopenharmony_ci wx_free_all_rx_resources(wx); 258262306a36Sopenharmony_cierr_free_tx: 258362306a36Sopenharmony_ci wx_free_all_tx_resources(wx); 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci return err; 258662306a36Sopenharmony_ci} 258762306a36Sopenharmony_ciEXPORT_SYMBOL(wx_setup_resources); 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci/** 259062306a36Sopenharmony_ci * wx_get_stats64 - Get System Network Statistics 259162306a36Sopenharmony_ci * @netdev: network interface device structure 259262306a36Sopenharmony_ci * @stats: storage space for 64bit statistics 259362306a36Sopenharmony_ci */ 259462306a36Sopenharmony_civoid wx_get_stats64(struct net_device *netdev, 259562306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 259662306a36Sopenharmony_ci{ 259762306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 259862306a36Sopenharmony_ci int i; 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci rcu_read_lock(); 260162306a36Sopenharmony_ci for (i = 0; i < wx->num_rx_queues; i++) { 260262306a36Sopenharmony_ci struct wx_ring *ring = READ_ONCE(wx->rx_ring[i]); 260362306a36Sopenharmony_ci u64 bytes, packets; 260462306a36Sopenharmony_ci unsigned int start; 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci if (ring) { 260762306a36Sopenharmony_ci do { 260862306a36Sopenharmony_ci start = u64_stats_fetch_begin(&ring->syncp); 260962306a36Sopenharmony_ci packets = ring->stats.packets; 261062306a36Sopenharmony_ci bytes = ring->stats.bytes; 261162306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&ring->syncp, start)); 261262306a36Sopenharmony_ci stats->rx_packets += packets; 261362306a36Sopenharmony_ci stats->rx_bytes += bytes; 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci } 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci for (i = 0; i < wx->num_tx_queues; i++) { 261862306a36Sopenharmony_ci struct wx_ring *ring = READ_ONCE(wx->tx_ring[i]); 261962306a36Sopenharmony_ci u64 bytes, packets; 262062306a36Sopenharmony_ci unsigned int start; 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci if (ring) { 262362306a36Sopenharmony_ci do { 262462306a36Sopenharmony_ci start = u64_stats_fetch_begin(&ring->syncp); 262562306a36Sopenharmony_ci packets = ring->stats.packets; 262662306a36Sopenharmony_ci bytes = ring->stats.bytes; 262762306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&ring->syncp, 262862306a36Sopenharmony_ci start)); 262962306a36Sopenharmony_ci stats->tx_packets += packets; 263062306a36Sopenharmony_ci stats->tx_bytes += bytes; 263162306a36Sopenharmony_ci } 263262306a36Sopenharmony_ci } 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci rcu_read_unlock(); 263562306a36Sopenharmony_ci} 263662306a36Sopenharmony_ciEXPORT_SYMBOL(wx_get_stats64); 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ciint wx_set_features(struct net_device *netdev, netdev_features_t features) 263962306a36Sopenharmony_ci{ 264062306a36Sopenharmony_ci netdev_features_t changed = netdev->features ^ features; 264162306a36Sopenharmony_ci struct wx *wx = netdev_priv(netdev); 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci if (changed & NETIF_F_RXHASH) 264462306a36Sopenharmony_ci wr32m(wx, WX_RDB_RA_CTL, WX_RDB_RA_CTL_RSS_EN, 264562306a36Sopenharmony_ci WX_RDB_RA_CTL_RSS_EN); 264662306a36Sopenharmony_ci else 264762306a36Sopenharmony_ci wr32m(wx, WX_RDB_RA_CTL, WX_RDB_RA_CTL_RSS_EN, 0); 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci if (changed & 265062306a36Sopenharmony_ci (NETIF_F_HW_VLAN_CTAG_RX | 265162306a36Sopenharmony_ci NETIF_F_HW_VLAN_STAG_RX)) 265262306a36Sopenharmony_ci wx_set_rx_mode(netdev); 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci return 1; 265562306a36Sopenharmony_ci} 265662306a36Sopenharmony_ciEXPORT_SYMBOL(wx_set_features); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2659