xref: /linux/drivers/net/ethernet/intel/libeth/rx.c (revision 359bcf15ec1d6738ede721db628594ecf05fd998)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (C) 2024-2025 Intel Corporation */
3 
4 #define DEFAULT_SYMBOL_NAMESPACE	"LIBETH"
5 
6 #include <linux/export.h>
7 
8 #include <net/libeth/rx.h>
9 
10 /* Rx buffer management */
11 
12 /**
13  * libeth_rx_hw_len_mtu - get the actual buffer size to be passed to HW
14  * @pp: &page_pool_params of the netdev to calculate the size for
15  * @max_len: maximum buffer size for a single descriptor
16  *
17  * Return: HW-writeable length per one buffer to pass it to the HW accounting:
18  * MTU the @dev has, HW required alignment, minimum and maximum allowed values,
19  * and system's page size.
20  */
21 static u32 libeth_rx_hw_len_mtu(const struct page_pool_params *pp, u32 max_len)
22 {
23 	u32 len;
24 
25 	len = READ_ONCE(pp->netdev->mtu) + LIBETH_RX_LL_LEN;
26 	len = ALIGN(len, LIBETH_RX_BUF_STRIDE);
27 	len = min3(len, ALIGN_DOWN(max_len ? : U32_MAX, LIBETH_RX_BUF_STRIDE),
28 		   pp->max_len);
29 
30 	return len;
31 }
32 
33 /**
34  * libeth_rx_hw_len_truesize - get the short buffer size to be passed to HW
35  * @pp: &page_pool_params of the netdev to calculate the size for
36  * @max_len: maximum buffer size for a single descriptor
37  * @truesize: desired truesize for the buffers
38  *
39  * Return: HW-writeable length per one buffer to pass it to the HW ignoring the
40  * MTU and closest to the passed truesize. Can be used for "short" buffer
41  * queues to fragment pages more efficiently.
42  */
43 static u32 libeth_rx_hw_len_truesize(const struct page_pool_params *pp,
44 				     u32 max_len, u32 truesize)
45 {
46 	u32 min, len;
47 
48 	min = SKB_HEAD_ALIGN(pp->offset + LIBETH_RX_BUF_STRIDE);
49 	truesize = clamp(roundup_pow_of_two(truesize), roundup_pow_of_two(min),
50 			 PAGE_SIZE << LIBETH_RX_PAGE_ORDER);
51 
52 	len = SKB_WITH_OVERHEAD(truesize - pp->offset);
53 	len = ALIGN_DOWN(len, LIBETH_RX_BUF_STRIDE) ? : LIBETH_RX_BUF_STRIDE;
54 	len = min3(len, ALIGN_DOWN(max_len ? : U32_MAX, LIBETH_RX_BUF_STRIDE),
55 		   pp->max_len);
56 
57 	return len;
58 }
59 
60 /**
61  * libeth_rx_page_pool_params - calculate params with the stack overhead
62  * @fq: buffer queue to calculate the size for
63  * @pp: &page_pool_params of the netdev
64  *
65  * Set the PP params to will all needed stack overhead (headroom, tailroom) and
66  * both the HW buffer length and the truesize for all types of buffers. For
67  * "short" buffers, truesize never exceeds the "wanted" one; for the rest,
68  * it can be up to the page size.
69  *
70  * Return: true on success, false on invalid input params.
71  */
72 static bool libeth_rx_page_pool_params(struct libeth_fq *fq,
73 				       struct page_pool_params *pp)
74 {
75 	pp->offset = LIBETH_SKB_HEADROOM;
76 	/* HW-writeable / syncable length per one page */
77 	pp->max_len = LIBETH_RX_PAGE_LEN(pp->offset);
78 
79 	/* HW-writeable length per buffer */
80 	switch (fq->type) {
81 	case LIBETH_FQE_MTU:
82 		fq->buf_len = libeth_rx_hw_len_mtu(pp, fq->buf_len);
83 		break;
84 	case LIBETH_FQE_SHORT:
85 		fq->buf_len = libeth_rx_hw_len_truesize(pp, fq->buf_len,
86 							fq->truesize);
87 		break;
88 	case LIBETH_FQE_HDR:
89 		fq->buf_len = ALIGN(LIBETH_MAX_HEAD, LIBETH_RX_BUF_STRIDE);
90 		break;
91 	default:
92 		return false;
93 	}
94 
95 	/* Buffer size to allocate */
96 	fq->truesize = roundup_pow_of_two(SKB_HEAD_ALIGN(pp->offset +
97 							 fq->buf_len));
98 
99 	return true;
100 }
101 
102 /**
103  * libeth_rx_page_pool_params_zc - calculate params without the stack overhead
104  * @fq: buffer queue to calculate the size for
105  * @pp: &page_pool_params of the netdev
106  *
107  * Set the PP params to exclude the stack overhead and both the buffer length
108  * and the truesize, which are equal for the data buffers. Note that this
109  * requires separate header buffers to be always active and account the
110  * overhead.
111  * With the MTU == ``PAGE_SIZE``, this allows the kernel to enable the zerocopy
112  * mode.
113  *
114  * Return: true on success, false on invalid input params.
115  */
116 static bool libeth_rx_page_pool_params_zc(struct libeth_fq *fq,
117 					  struct page_pool_params *pp)
118 {
119 	u32 mtu, max;
120 
121 	pp->offset = 0;
122 	pp->max_len = PAGE_SIZE << LIBETH_RX_PAGE_ORDER;
123 
124 	switch (fq->type) {
125 	case LIBETH_FQE_MTU:
126 		mtu = READ_ONCE(pp->netdev->mtu);
127 		break;
128 	case LIBETH_FQE_SHORT:
129 		mtu = fq->truesize;
130 		break;
131 	default:
132 		return false;
133 	}
134 
135 	mtu = roundup_pow_of_two(mtu);
136 	max = min(rounddown_pow_of_two(fq->buf_len ? : U32_MAX),
137 		  pp->max_len);
138 
139 	fq->buf_len = clamp(mtu, LIBETH_RX_BUF_STRIDE, max);
140 	fq->truesize = fq->buf_len;
141 
142 	return true;
143 }
144 
145 /**
146  * libeth_rx_fq_create - create a PP with the default libeth settings
147  * @fq: buffer queue struct to fill
148  * @napi: &napi_struct covering this PP (no usage outside its poll loops)
149  *
150  * Return: %0 on success, -%errno on failure.
151  */
152 int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi)
153 {
154 	struct page_pool_params pp = {
155 		.flags		= PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
156 		.order		= LIBETH_RX_PAGE_ORDER,
157 		.pool_size	= fq->count,
158 		.nid		= fq->nid,
159 		.dev		= napi->dev->dev.parent,
160 		.netdev		= napi->dev,
161 		.napi		= napi,
162 		.dma_dir	= DMA_FROM_DEVICE,
163 	};
164 	struct libeth_fqe *fqes;
165 	struct page_pool *pool;
166 	bool ret;
167 
168 	if (!fq->hsplit)
169 		ret = libeth_rx_page_pool_params(fq, &pp);
170 	else
171 		ret = libeth_rx_page_pool_params_zc(fq, &pp);
172 	if (!ret)
173 		return -EINVAL;
174 
175 	pool = page_pool_create(&pp);
176 	if (IS_ERR(pool))
177 		return PTR_ERR(pool);
178 
179 	fqes = kvcalloc_node(fq->count, sizeof(*fqes), GFP_KERNEL, fq->nid);
180 	if (!fqes)
181 		goto err_buf;
182 
183 	fq->fqes = fqes;
184 	fq->pp = pool;
185 
186 	return 0;
187 
188 err_buf:
189 	page_pool_destroy(pool);
190 
191 	return -ENOMEM;
192 }
193 EXPORT_SYMBOL_GPL(libeth_rx_fq_create);
194 
195 /**
196  * libeth_rx_fq_destroy - destroy a &page_pool created by libeth
197  * @fq: buffer queue to process
198  */
199 void libeth_rx_fq_destroy(struct libeth_fq *fq)
200 {
201 	kvfree(fq->fqes);
202 	page_pool_destroy(fq->pp);
203 }
204 EXPORT_SYMBOL_GPL(libeth_rx_fq_destroy);
205 
206 /**
207  * libeth_rx_recycle_slow - recycle a libeth page from the NAPI context
208  * @page: page to recycle
209  *
210  * To be used on exceptions or rare cases not requiring fast inline recycling.
211  */
212 void libeth_rx_recycle_slow(struct page *page)
213 {
214 	page_pool_recycle_direct(page->pp, page);
215 }
216 EXPORT_SYMBOL_GPL(libeth_rx_recycle_slow);
217 
218 /* Converting abstract packet type numbers into a software structure with
219  * the packet parameters to do O(1) lookup on Rx.
220  */
221 
222 static const u16 libeth_rx_pt_xdp_oip[] = {
223 	[LIBETH_RX_PT_OUTER_L2]		= XDP_RSS_TYPE_NONE,
224 	[LIBETH_RX_PT_OUTER_IPV4]	= XDP_RSS_L3_IPV4,
225 	[LIBETH_RX_PT_OUTER_IPV6]	= XDP_RSS_L3_IPV6,
226 };
227 
228 static const u16 libeth_rx_pt_xdp_iprot[] = {
229 	[LIBETH_RX_PT_INNER_NONE]	= XDP_RSS_TYPE_NONE,
230 	[LIBETH_RX_PT_INNER_UDP]	= XDP_RSS_L4_UDP,
231 	[LIBETH_RX_PT_INNER_TCP]	= XDP_RSS_L4_TCP,
232 	[LIBETH_RX_PT_INNER_SCTP]	= XDP_RSS_L4_SCTP,
233 	[LIBETH_RX_PT_INNER_ICMP]	= XDP_RSS_L4_ICMP,
234 	[LIBETH_RX_PT_INNER_TIMESYNC]	= XDP_RSS_TYPE_NONE,
235 };
236 
237 static const u16 libeth_rx_pt_xdp_pl[] = {
238 	[LIBETH_RX_PT_PAYLOAD_NONE]	= XDP_RSS_TYPE_NONE,
239 	[LIBETH_RX_PT_PAYLOAD_L2]	= XDP_RSS_TYPE_NONE,
240 	[LIBETH_RX_PT_PAYLOAD_L3]	= XDP_RSS_TYPE_NONE,
241 	[LIBETH_RX_PT_PAYLOAD_L4]	= XDP_RSS_L4,
242 };
243 
244 /**
245  * libeth_rx_pt_gen_hash_type - generate an XDP RSS hash type for a PT
246  * @pt: PT structure to evaluate
247  *
248  * Generates ```hash_type``` field with XDP RSS type values from the parsed
249  * packet parameters if they're obtained dynamically at runtime.
250  */
251 void libeth_rx_pt_gen_hash_type(struct libeth_rx_pt *pt)
252 {
253 	pt->hash_type = 0;
254 	pt->hash_type |= libeth_rx_pt_xdp_oip[pt->outer_ip];
255 	pt->hash_type |= libeth_rx_pt_xdp_iprot[pt->inner_prot];
256 	pt->hash_type |= libeth_rx_pt_xdp_pl[pt->payload_layer];
257 }
258 EXPORT_SYMBOL_GPL(libeth_rx_pt_gen_hash_type);
259 
260 /* Module */
261 
262 MODULE_DESCRIPTION("Common Ethernet library");
263 MODULE_LICENSE("GPL");
264