1306ec721SAlexander Lobakin /* SPDX-License-Identifier: GPL-2.0-only */
2306ec721SAlexander Lobakin /* Copyright (C) 2024 Intel Corporation */
3306ec721SAlexander Lobakin
4306ec721SAlexander Lobakin #ifndef __LIBETH_RX_H
5306ec721SAlexander Lobakin #define __LIBETH_RX_H
6306ec721SAlexander Lobakin
7e6c91556SAlexander Lobakin #include <linux/if_vlan.h>
8e6c91556SAlexander Lobakin
9e6c91556SAlexander Lobakin #include <net/page_pool/helpers.h>
10306ec721SAlexander Lobakin #include <net/xdp.h>
11306ec721SAlexander Lobakin
12e6c91556SAlexander Lobakin /* Rx buffer management */
13e6c91556SAlexander Lobakin
14e6c91556SAlexander Lobakin /* Space reserved in front of each frame */
15e6c91556SAlexander Lobakin #define LIBETH_SKB_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN)
16e6c91556SAlexander Lobakin /* Maximum headroom for worst-case calculations */
17e6c91556SAlexander Lobakin #define LIBETH_MAX_HEADROOM LIBETH_SKB_HEADROOM
18e6c91556SAlexander Lobakin /* Link layer / L2 overhead: Ethernet, 2 VLAN tags (C + S), FCS */
19e6c91556SAlexander Lobakin #define LIBETH_RX_LL_LEN (ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN)
205aaac1aeSAlexander Lobakin /* Maximum supported L2-L4 header length */
215aaac1aeSAlexander Lobakin #define LIBETH_MAX_HEAD roundup_pow_of_two(max(MAX_HEADER, 256))
22e6c91556SAlexander Lobakin
23e6c91556SAlexander Lobakin /* Always use order-0 pages */
24e6c91556SAlexander Lobakin #define LIBETH_RX_PAGE_ORDER 0
25e6c91556SAlexander Lobakin /* Pick a sane buffer stride and align to a cacheline boundary */
26e6c91556SAlexander Lobakin #define LIBETH_RX_BUF_STRIDE SKB_DATA_ALIGN(128)
27e6c91556SAlexander Lobakin /* HW-writeable space in one buffer: truesize - headroom/tailroom, aligned */
28e6c91556SAlexander Lobakin #define LIBETH_RX_PAGE_LEN(hr) \
29e6c91556SAlexander Lobakin ALIGN_DOWN(SKB_MAX_ORDER(hr, LIBETH_RX_PAGE_ORDER), \
30e6c91556SAlexander Lobakin LIBETH_RX_BUF_STRIDE)
31e6c91556SAlexander Lobakin
32e6c91556SAlexander Lobakin /**
33e6c91556SAlexander Lobakin * struct libeth_fqe - structure representing an Rx buffer (fill queue element)
34e6c91556SAlexander Lobakin * @page: page holding the buffer
35e6c91556SAlexander Lobakin * @offset: offset from the page start (to the headroom)
36e6c91556SAlexander Lobakin * @truesize: total space occupied by the buffer (w/ headroom and tailroom)
37e6c91556SAlexander Lobakin *
38e6c91556SAlexander Lobakin * Depending on the MTU, API switches between one-page-per-frame and shared
39e6c91556SAlexander Lobakin * page model (to conserve memory on bigger-page platforms). In case of the
40e6c91556SAlexander Lobakin * former, @offset is always 0 and @truesize is always ```PAGE_SIZE```.
41e6c91556SAlexander Lobakin */
42e6c91556SAlexander Lobakin struct libeth_fqe {
43e6c91556SAlexander Lobakin struct page *page;
44e6c91556SAlexander Lobakin u32 offset;
45e6c91556SAlexander Lobakin u32 truesize;
46e6c91556SAlexander Lobakin } __aligned_largest;
47e6c91556SAlexander Lobakin
48e6c91556SAlexander Lobakin /**
495aaac1aeSAlexander Lobakin * enum libeth_fqe_type - enum representing types of Rx buffers
505aaac1aeSAlexander Lobakin * @LIBETH_FQE_MTU: buffer size is determined by MTU
515aaac1aeSAlexander Lobakin * @LIBETH_FQE_SHORT: buffer size is smaller than MTU, for short frames
525aaac1aeSAlexander Lobakin * @LIBETH_FQE_HDR: buffer size is ```LIBETH_MAX_HEAD```-sized, for headers
535aaac1aeSAlexander Lobakin */
545aaac1aeSAlexander Lobakin enum libeth_fqe_type {
555aaac1aeSAlexander Lobakin LIBETH_FQE_MTU = 0U,
565aaac1aeSAlexander Lobakin LIBETH_FQE_SHORT,
575aaac1aeSAlexander Lobakin LIBETH_FQE_HDR,
585aaac1aeSAlexander Lobakin };
595aaac1aeSAlexander Lobakin
605aaac1aeSAlexander Lobakin /**
61e6c91556SAlexander Lobakin * struct libeth_fq - structure representing a buffer (fill) queue
62e6c91556SAlexander Lobakin * @fp: hotpath part of the structure
63e6c91556SAlexander Lobakin * @pp: &page_pool for buffer management
64e6c91556SAlexander Lobakin * @fqes: array of Rx buffers
65e6c91556SAlexander Lobakin * @truesize: size to allocate per buffer, w/overhead
66e6c91556SAlexander Lobakin * @count: number of descriptors/buffers the queue has
675aaac1aeSAlexander Lobakin * @type: type of the buffers this queue has
685aaac1aeSAlexander Lobakin * @hsplit: flag whether header split is enabled
69e6c91556SAlexander Lobakin * @buf_len: HW-writeable length per each buffer
70e6c91556SAlexander Lobakin * @nid: ID of the closest NUMA node with memory
71e6c91556SAlexander Lobakin */
72e6c91556SAlexander Lobakin struct libeth_fq {
73e6c91556SAlexander Lobakin struct_group_tagged(libeth_fq_fp, fp,
74e6c91556SAlexander Lobakin struct page_pool *pp;
75e6c91556SAlexander Lobakin struct libeth_fqe *fqes;
76e6c91556SAlexander Lobakin
77e6c91556SAlexander Lobakin u32 truesize;
78e6c91556SAlexander Lobakin u32 count;
79e6c91556SAlexander Lobakin );
80e6c91556SAlexander Lobakin
81e6c91556SAlexander Lobakin /* Cold fields */
825aaac1aeSAlexander Lobakin enum libeth_fqe_type type:2;
835aaac1aeSAlexander Lobakin bool hsplit:1;
845aaac1aeSAlexander Lobakin
85e6c91556SAlexander Lobakin u32 buf_len;
86e6c91556SAlexander Lobakin int nid;
87e6c91556SAlexander Lobakin };
88e6c91556SAlexander Lobakin
89e6c91556SAlexander Lobakin int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi);
90e6c91556SAlexander Lobakin void libeth_rx_fq_destroy(struct libeth_fq *fq);
91e6c91556SAlexander Lobakin
92e6c91556SAlexander Lobakin /**
93e6c91556SAlexander Lobakin * libeth_rx_alloc - allocate a new Rx buffer
94e6c91556SAlexander Lobakin * @fq: fill queue to allocate for
95e6c91556SAlexander Lobakin * @i: index of the buffer within the queue
96e6c91556SAlexander Lobakin *
97e6c91556SAlexander Lobakin * Return: DMA address to be passed to HW for Rx on successful allocation,
98e6c91556SAlexander Lobakin * ```DMA_MAPPING_ERROR``` otherwise.
99e6c91556SAlexander Lobakin */
libeth_rx_alloc(const struct libeth_fq_fp * fq,u32 i)100e6c91556SAlexander Lobakin static inline dma_addr_t libeth_rx_alloc(const struct libeth_fq_fp *fq, u32 i)
101e6c91556SAlexander Lobakin {
102e6c91556SAlexander Lobakin struct libeth_fqe *buf = &fq->fqes[i];
103e6c91556SAlexander Lobakin
104e6c91556SAlexander Lobakin buf->truesize = fq->truesize;
105e6c91556SAlexander Lobakin buf->page = page_pool_dev_alloc(fq->pp, &buf->offset, &buf->truesize);
106e6c91556SAlexander Lobakin if (unlikely(!buf->page))
107e6c91556SAlexander Lobakin return DMA_MAPPING_ERROR;
108e6c91556SAlexander Lobakin
109e6c91556SAlexander Lobakin return page_pool_get_dma_addr(buf->page) + buf->offset +
110e6c91556SAlexander Lobakin fq->pp->p.offset;
111e6c91556SAlexander Lobakin }
112e6c91556SAlexander Lobakin
113e6c91556SAlexander Lobakin void libeth_rx_recycle_slow(struct page *page);
114e6c91556SAlexander Lobakin
115e6c91556SAlexander Lobakin /**
116e6c91556SAlexander Lobakin * libeth_rx_sync_for_cpu - synchronize or recycle buffer post DMA
117e6c91556SAlexander Lobakin * @fqe: buffer to process
118e6c91556SAlexander Lobakin * @len: frame length from the descriptor
119e6c91556SAlexander Lobakin *
120e6c91556SAlexander Lobakin * Process the buffer after it's written by HW. The regular path is to
121e6c91556SAlexander Lobakin * synchronize DMA for CPU, but in case of no data it will be immediately
122e6c91556SAlexander Lobakin * recycled back to its PP.
123e6c91556SAlexander Lobakin *
124e6c91556SAlexander Lobakin * Return: true when there's data to process, false otherwise.
125e6c91556SAlexander Lobakin */
libeth_rx_sync_for_cpu(const struct libeth_fqe * fqe,u32 len)126e6c91556SAlexander Lobakin static inline bool libeth_rx_sync_for_cpu(const struct libeth_fqe *fqe,
127e6c91556SAlexander Lobakin u32 len)
128e6c91556SAlexander Lobakin {
129e6c91556SAlexander Lobakin struct page *page = fqe->page;
130e6c91556SAlexander Lobakin
131e6c91556SAlexander Lobakin /* Very rare, but possible case. The most common reason:
132e6c91556SAlexander Lobakin * the last fragment contained FCS only, which was then
133e6c91556SAlexander Lobakin * stripped by the HW.
134e6c91556SAlexander Lobakin */
135e6c91556SAlexander Lobakin if (unlikely(!len)) {
136e6c91556SAlexander Lobakin libeth_rx_recycle_slow(page);
137e6c91556SAlexander Lobakin return false;
138e6c91556SAlexander Lobakin }
139e6c91556SAlexander Lobakin
140e6c91556SAlexander Lobakin page_pool_dma_sync_for_cpu(page->pp, page, fqe->offset, len);
141e6c91556SAlexander Lobakin
142e6c91556SAlexander Lobakin return true;
143e6c91556SAlexander Lobakin }
144e6c91556SAlexander Lobakin
145306ec721SAlexander Lobakin /* Converting abstract packet type numbers into a software structure with
146306ec721SAlexander Lobakin * the packet parameters to do O(1) lookup on Rx.
147306ec721SAlexander Lobakin */
148306ec721SAlexander Lobakin
149306ec721SAlexander Lobakin enum {
150306ec721SAlexander Lobakin LIBETH_RX_PT_OUTER_L2 = 0U,
151306ec721SAlexander Lobakin LIBETH_RX_PT_OUTER_IPV4,
152306ec721SAlexander Lobakin LIBETH_RX_PT_OUTER_IPV6,
153306ec721SAlexander Lobakin };
154306ec721SAlexander Lobakin
155306ec721SAlexander Lobakin enum {
156306ec721SAlexander Lobakin LIBETH_RX_PT_NOT_FRAG = 0U,
157306ec721SAlexander Lobakin LIBETH_RX_PT_FRAG,
158306ec721SAlexander Lobakin };
159306ec721SAlexander Lobakin
160306ec721SAlexander Lobakin enum {
161306ec721SAlexander Lobakin LIBETH_RX_PT_TUNNEL_IP_NONE = 0U,
162306ec721SAlexander Lobakin LIBETH_RX_PT_TUNNEL_IP_IP,
163306ec721SAlexander Lobakin LIBETH_RX_PT_TUNNEL_IP_GRENAT,
164306ec721SAlexander Lobakin LIBETH_RX_PT_TUNNEL_IP_GRENAT_MAC,
165306ec721SAlexander Lobakin LIBETH_RX_PT_TUNNEL_IP_GRENAT_MAC_VLAN,
166306ec721SAlexander Lobakin };
167306ec721SAlexander Lobakin
168306ec721SAlexander Lobakin enum {
169306ec721SAlexander Lobakin LIBETH_RX_PT_TUNNEL_END_NONE = 0U,
170306ec721SAlexander Lobakin LIBETH_RX_PT_TUNNEL_END_IPV4,
171306ec721SAlexander Lobakin LIBETH_RX_PT_TUNNEL_END_IPV6,
172306ec721SAlexander Lobakin };
173306ec721SAlexander Lobakin
174306ec721SAlexander Lobakin enum {
175306ec721SAlexander Lobakin LIBETH_RX_PT_INNER_NONE = 0U,
176306ec721SAlexander Lobakin LIBETH_RX_PT_INNER_UDP,
177306ec721SAlexander Lobakin LIBETH_RX_PT_INNER_TCP,
178306ec721SAlexander Lobakin LIBETH_RX_PT_INNER_SCTP,
179306ec721SAlexander Lobakin LIBETH_RX_PT_INNER_ICMP,
180306ec721SAlexander Lobakin LIBETH_RX_PT_INNER_TIMESYNC,
181306ec721SAlexander Lobakin };
182306ec721SAlexander Lobakin
183306ec721SAlexander Lobakin #define LIBETH_RX_PT_PAYLOAD_NONE PKT_HASH_TYPE_NONE
184306ec721SAlexander Lobakin #define LIBETH_RX_PT_PAYLOAD_L2 PKT_HASH_TYPE_L2
185306ec721SAlexander Lobakin #define LIBETH_RX_PT_PAYLOAD_L3 PKT_HASH_TYPE_L3
186306ec721SAlexander Lobakin #define LIBETH_RX_PT_PAYLOAD_L4 PKT_HASH_TYPE_L4
187306ec721SAlexander Lobakin
188306ec721SAlexander Lobakin struct libeth_rx_pt {
189306ec721SAlexander Lobakin u32 outer_ip:2;
190306ec721SAlexander Lobakin u32 outer_frag:1;
191306ec721SAlexander Lobakin u32 tunnel_type:3;
192306ec721SAlexander Lobakin u32 tunnel_end_prot:2;
193306ec721SAlexander Lobakin u32 tunnel_end_frag:1;
194306ec721SAlexander Lobakin u32 inner_prot:3;
195306ec721SAlexander Lobakin enum pkt_hash_types payload_layer:2;
196306ec721SAlexander Lobakin
197306ec721SAlexander Lobakin u32 pad:2;
198306ec721SAlexander Lobakin enum xdp_rss_hash_type hash_type:16;
199306ec721SAlexander Lobakin };
200306ec721SAlexander Lobakin
201*ce5cf4afSMateusz Polchlopek /**
202*ce5cf4afSMateusz Polchlopek * struct libeth_rx_csum - checksum offload bits decoded from the Rx descriptor
203*ce5cf4afSMateusz Polchlopek * @l3l4p: detectable L3 and L4 integrity check is processed by the hardware
204*ce5cf4afSMateusz Polchlopek * @ipe: IP checksum error
205*ce5cf4afSMateusz Polchlopek * @eipe: external (outermost) IP header (only for tunels)
206*ce5cf4afSMateusz Polchlopek * @eudpe: external (outermost) UDP checksum error (only for tunels)
207*ce5cf4afSMateusz Polchlopek * @ipv6exadd: IPv6 header with extension headers
208*ce5cf4afSMateusz Polchlopek * @l4e: L4 integrity error
209*ce5cf4afSMateusz Polchlopek * @pprs: set for packets that skip checksum calculation in the HW pre parser
210*ce5cf4afSMateusz Polchlopek * @nat: the packet is a UDP tunneled packet
211*ce5cf4afSMateusz Polchlopek * @raw_csum_valid: set if raw checksum is valid
212*ce5cf4afSMateusz Polchlopek * @pad: padding to naturally align raw_csum field
213*ce5cf4afSMateusz Polchlopek * @raw_csum: raw checksum
214*ce5cf4afSMateusz Polchlopek */
215*ce5cf4afSMateusz Polchlopek struct libeth_rx_csum {
216*ce5cf4afSMateusz Polchlopek u32 l3l4p:1;
217*ce5cf4afSMateusz Polchlopek u32 ipe:1;
218*ce5cf4afSMateusz Polchlopek u32 eipe:1;
219*ce5cf4afSMateusz Polchlopek u32 eudpe:1;
220*ce5cf4afSMateusz Polchlopek u32 ipv6exadd:1;
221*ce5cf4afSMateusz Polchlopek u32 l4e:1;
222*ce5cf4afSMateusz Polchlopek u32 pprs:1;
223*ce5cf4afSMateusz Polchlopek u32 nat:1;
224*ce5cf4afSMateusz Polchlopek
225*ce5cf4afSMateusz Polchlopek u32 raw_csum_valid:1;
226*ce5cf4afSMateusz Polchlopek u32 pad:7;
227*ce5cf4afSMateusz Polchlopek u32 raw_csum:16;
228*ce5cf4afSMateusz Polchlopek };
229*ce5cf4afSMateusz Polchlopek
230*ce5cf4afSMateusz Polchlopek /**
231*ce5cf4afSMateusz Polchlopek * struct libeth_rqe_info - receive queue element info
232*ce5cf4afSMateusz Polchlopek * @len: packet length
233*ce5cf4afSMateusz Polchlopek * @ptype: packet type based on types programmed into the device
234*ce5cf4afSMateusz Polchlopek * @eop: whether it's the last fragment of the packet
235*ce5cf4afSMateusz Polchlopek * @rxe: MAC errors: CRC, Alignment, Oversize, Undersizes, Length error
236*ce5cf4afSMateusz Polchlopek * @vlan: C-VLAN or S-VLAN tag depending on the VLAN offload configuration
237*ce5cf4afSMateusz Polchlopek */
238*ce5cf4afSMateusz Polchlopek struct libeth_rqe_info {
239*ce5cf4afSMateusz Polchlopek u32 len;
240*ce5cf4afSMateusz Polchlopek
241*ce5cf4afSMateusz Polchlopek u32 ptype:14;
242*ce5cf4afSMateusz Polchlopek u32 eop:1;
243*ce5cf4afSMateusz Polchlopek u32 rxe:1;
244*ce5cf4afSMateusz Polchlopek
245*ce5cf4afSMateusz Polchlopek u32 vlan:16;
246*ce5cf4afSMateusz Polchlopek };
247*ce5cf4afSMateusz Polchlopek
248306ec721SAlexander Lobakin void libeth_rx_pt_gen_hash_type(struct libeth_rx_pt *pt);
249306ec721SAlexander Lobakin
250306ec721SAlexander Lobakin /**
251306ec721SAlexander Lobakin * libeth_rx_pt_get_ip_ver - get IP version from a packet type structure
252306ec721SAlexander Lobakin * @pt: packet type params
253306ec721SAlexander Lobakin *
254306ec721SAlexander Lobakin * Wrapper to compile out the IPv6 code from the drivers when not supported
255306ec721SAlexander Lobakin * by the kernel.
256306ec721SAlexander Lobakin *
257306ec721SAlexander Lobakin * Return: @pt.outer_ip or stub for IPv6 when not compiled-in.
258306ec721SAlexander Lobakin */
libeth_rx_pt_get_ip_ver(struct libeth_rx_pt pt)259306ec721SAlexander Lobakin static inline u32 libeth_rx_pt_get_ip_ver(struct libeth_rx_pt pt)
260306ec721SAlexander Lobakin {
261306ec721SAlexander Lobakin #if !IS_ENABLED(CONFIG_IPV6)
262306ec721SAlexander Lobakin switch (pt.outer_ip) {
263306ec721SAlexander Lobakin case LIBETH_RX_PT_OUTER_IPV4:
264306ec721SAlexander Lobakin return LIBETH_RX_PT_OUTER_IPV4;
265306ec721SAlexander Lobakin default:
266306ec721SAlexander Lobakin return LIBETH_RX_PT_OUTER_L2;
267306ec721SAlexander Lobakin }
268306ec721SAlexander Lobakin #else
269306ec721SAlexander Lobakin return pt.outer_ip;
270306ec721SAlexander Lobakin #endif
271306ec721SAlexander Lobakin }
272306ec721SAlexander Lobakin
273306ec721SAlexander Lobakin /* libeth_has_*() can be used to quickly check whether the HW metadata is
274306ec721SAlexander Lobakin * available to avoid further expensive processing such as descriptor reads.
275306ec721SAlexander Lobakin * They already check for the corresponding netdev feature to be enabled,
276306ec721SAlexander Lobakin * thus can be used as drop-in replacements.
277306ec721SAlexander Lobakin */
278306ec721SAlexander Lobakin
libeth_rx_pt_has_checksum(const struct net_device * dev,struct libeth_rx_pt pt)279306ec721SAlexander Lobakin static inline bool libeth_rx_pt_has_checksum(const struct net_device *dev,
280306ec721SAlexander Lobakin struct libeth_rx_pt pt)
281306ec721SAlexander Lobakin {
282306ec721SAlexander Lobakin /* Non-zero _INNER* is only possible when _OUTER_IPV* is set,
283306ec721SAlexander Lobakin * it is enough to check only for the L4 type.
284306ec721SAlexander Lobakin */
285306ec721SAlexander Lobakin return likely(pt.inner_prot > LIBETH_RX_PT_INNER_NONE &&
286306ec721SAlexander Lobakin (dev->features & NETIF_F_RXCSUM));
287306ec721SAlexander Lobakin }
288306ec721SAlexander Lobakin
libeth_rx_pt_has_hash(const struct net_device * dev,struct libeth_rx_pt pt)289306ec721SAlexander Lobakin static inline bool libeth_rx_pt_has_hash(const struct net_device *dev,
290306ec721SAlexander Lobakin struct libeth_rx_pt pt)
291306ec721SAlexander Lobakin {
292306ec721SAlexander Lobakin return likely(pt.payload_layer > LIBETH_RX_PT_PAYLOAD_NONE &&
293306ec721SAlexander Lobakin (dev->features & NETIF_F_RXHASH));
294306ec721SAlexander Lobakin }
295306ec721SAlexander Lobakin
296306ec721SAlexander Lobakin /**
297306ec721SAlexander Lobakin * libeth_rx_pt_set_hash - fill in skb hash value basing on the PT
298306ec721SAlexander Lobakin * @skb: skb to fill the hash in
299306ec721SAlexander Lobakin * @hash: 32-bit hash value from the descriptor
300306ec721SAlexander Lobakin * @pt: packet type
301306ec721SAlexander Lobakin */
libeth_rx_pt_set_hash(struct sk_buff * skb,u32 hash,struct libeth_rx_pt pt)302306ec721SAlexander Lobakin static inline void libeth_rx_pt_set_hash(struct sk_buff *skb, u32 hash,
303306ec721SAlexander Lobakin struct libeth_rx_pt pt)
304306ec721SAlexander Lobakin {
305306ec721SAlexander Lobakin skb_set_hash(skb, hash, pt.payload_layer);
306306ec721SAlexander Lobakin }
307306ec721SAlexander Lobakin
308306ec721SAlexander Lobakin #endif /* __LIBETH_RX_H */
309