xref: /dpdk/lib/pdcp/rte_pdcp.c (revision 08966fe7f79fdaa5019b7559eebe84a1e3787b89)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2023 Marvell.
3  */
4 
5 #include <stdalign.h>
6 
7 #include <rte_errno.h>
8 #include <rte_pdcp.h>
9 #include <rte_malloc.h>
10 
11 #include "pdcp_cnt.h"
12 #include "pdcp_crypto.h"
13 #include "pdcp_ctrl_pdu.h"
14 #include "pdcp_entity.h"
15 #include "pdcp_process.h"
16 
17 #define RTE_PDCP_DYNFIELD_NAME "rte_pdcp_dynfield"
18 
19 struct entity_layout {
20 	size_t bitmap_offset;
21 	size_t bitmap_size;
22 
23 	size_t reorder_buf_offset;
24 	size_t reorder_buf_size;
25 
26 	size_t total_size;
27 };
28 
29 int rte_pdcp_dynfield_offset = -1;
30 
31 static int
pdcp_dynfield_register(void)32 pdcp_dynfield_register(void)
33 {
34 	const struct rte_mbuf_dynfield dynfield_desc = {
35 		.name = RTE_PDCP_DYNFIELD_NAME,
36 		.size = sizeof(rte_pdcp_dynfield_t),
37 		.align = alignof(rte_pdcp_dynfield_t),
38 	};
39 
40 	if (rte_pdcp_dynfield_offset != -1)
41 		return rte_pdcp_dynfield_offset;
42 
43 	rte_pdcp_dynfield_offset = rte_mbuf_dynfield_register(&dynfield_desc);
44 	return rte_pdcp_dynfield_offset;
45 }
46 
47 static int
pdcp_entity_layout_get(const struct rte_pdcp_entity_conf * conf,struct entity_layout * layout)48 pdcp_entity_layout_get(const struct rte_pdcp_entity_conf *conf, struct entity_layout *layout)
49 {
50 	size_t size;
51 	const uint32_t window_size = pdcp_window_size_get(conf->pdcp_xfrm.sn_size);
52 
53 	size = sizeof(struct rte_pdcp_entity) + sizeof(struct entity_priv);
54 
55 	if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) {
56 		size += sizeof(struct entity_priv_dl_part);
57 		/* Bitmap require memory to be cache aligned */
58 		size = RTE_CACHE_LINE_ROUNDUP(size);
59 		layout->bitmap_offset = size;
60 		layout->bitmap_size = pdcp_cnt_bitmap_get_memory_footprint(conf);
61 		size += layout->bitmap_size;
62 		layout->reorder_buf_offset = size;
63 		layout->reorder_buf_size = pdcp_reorder_memory_footprint_get(window_size);
64 		size += layout->reorder_buf_size;
65 	} else if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK)
66 		size += sizeof(struct entity_priv_ul_part);
67 	else
68 		return -EINVAL;
69 
70 	layout->total_size = size;
71 
72 	return 0;
73 }
74 
75 static int
pdcp_dl_establish(struct rte_pdcp_entity * entity,const struct rte_pdcp_entity_conf * conf,const struct entity_layout * layout)76 pdcp_dl_establish(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf,
77 		  const struct entity_layout *layout)
78 {
79 	const uint32_t window_size = pdcp_window_size_get(conf->pdcp_xfrm.sn_size);
80 	struct entity_priv_dl_part *dl = entity_dl_part_get(entity);
81 	void *memory;
82 	int ret;
83 
84 	entity->max_pkt_cache = RTE_MAX(entity->max_pkt_cache, window_size);
85 	dl->t_reorder.handle = conf->t_reordering;
86 
87 	memory = RTE_PTR_ADD(entity, layout->reorder_buf_offset);
88 	ret = pdcp_reorder_create(&dl->reorder, window_size, memory, layout->reorder_buf_size);
89 	if (ret)
90 		return ret;
91 
92 	memory = RTE_PTR_ADD(entity, layout->bitmap_offset);
93 	ret = pdcp_cnt_bitmap_create(dl, window_size, memory, layout->bitmap_size);
94 	if (ret)
95 		return ret;
96 
97 	return 0;
98 }
99 
100 struct rte_pdcp_entity *
rte_pdcp_entity_establish(const struct rte_pdcp_entity_conf * conf)101 rte_pdcp_entity_establish(const struct rte_pdcp_entity_conf *conf)
102 {
103 	struct entity_layout entity_layout = { 0 };
104 	struct rte_pdcp_entity *entity = NULL;
105 	struct entity_priv *en_priv;
106 	uint32_t count;
107 	int ret;
108 
109 	if (pdcp_dynfield_register() < 0)
110 		return NULL;
111 
112 	if (conf == NULL || conf->cop_pool == NULL || conf->ctrl_pdu_pool == NULL) {
113 		rte_errno = EINVAL;
114 		return NULL;
115 	}
116 
117 	if (conf->pdcp_xfrm.en_ordering || conf->pdcp_xfrm.remove_duplicates || conf->is_slrb ||
118 	    conf->en_sec_offload) {
119 		rte_errno = ENOTSUP;
120 		return NULL;
121 	}
122 
123 	/*
124 	 * 6.3.2 PDCP SN
125 	 * Length: 12 or 18 bits as indicated in table 6.3.2-1. The length of the PDCP SN is
126 	 * configured by upper layers (pdcp-SN-SizeUL, pdcp-SN-SizeDL, or sl-PDCP-SN-Size in
127 	 * TS 38.331 [3])
128 	 */
129 	if ((conf->pdcp_xfrm.sn_size != RTE_SECURITY_PDCP_SN_SIZE_12) &&
130 	    (conf->pdcp_xfrm.sn_size != RTE_SECURITY_PDCP_SN_SIZE_18)) {
131 		rte_errno = ENOTSUP;
132 		return NULL;
133 	}
134 
135 	if (conf->pdcp_xfrm.hfn_threshold) {
136 		rte_errno = EINVAL;
137 		return NULL;
138 	}
139 
140 	ret = pdcp_entity_layout_get(conf, &entity_layout);
141 	if (ret < 0) {
142 		rte_errno = EINVAL;
143 		return NULL;
144 	}
145 
146 	entity = rte_zmalloc_socket("pdcp_entity", entity_layout.total_size, RTE_CACHE_LINE_SIZE,
147 				    SOCKET_ID_ANY);
148 	if (entity == NULL) {
149 		rte_errno = ENOMEM;
150 		return NULL;
151 	}
152 
153 	en_priv = entity_priv_get(entity);
154 
155 	count = pdcp_count_from_hfn_sn_get(conf->pdcp_xfrm.hfn, conf->sn, conf->pdcp_xfrm.sn_size);
156 
157 	en_priv->state.rx_deliv = count;
158 	en_priv->state.tx_next = count;
159 	en_priv->cop_pool = conf->cop_pool;
160 	en_priv->ctrl_pdu_pool = conf->ctrl_pdu_pool;
161 
162 	/* Setup crypto session */
163 	ret = pdcp_crypto_sess_create(entity, conf);
164 	if (ret)
165 		goto entity_free;
166 
167 	ret = pdcp_process_func_set(entity, conf);
168 	if (ret)
169 		goto crypto_sess_destroy;
170 
171 	if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) {
172 		ret = pdcp_dl_establish(entity, conf, &entity_layout);
173 		if (ret)
174 			goto crypto_sess_destroy;
175 	}
176 
177 	return entity;
178 
179 crypto_sess_destroy:
180 	pdcp_crypto_sess_destroy(entity);
181 entity_free:
182 	rte_free(entity);
183 	rte_errno = -ret;
184 	return NULL;
185 }
186 
187 static int
pdcp_dl_release(struct rte_pdcp_entity * entity,struct rte_mbuf * out_mb[])188 pdcp_dl_release(struct rte_pdcp_entity *entity, struct rte_mbuf *out_mb[])
189 {
190 	struct entity_priv_dl_part *dl = entity_dl_part_get(entity);
191 	struct entity_priv *en_priv = entity_priv_get(entity);
192 	int nb_out;
193 
194 	nb_out = pdcp_reorder_up_to_get(&dl->reorder, out_mb, entity->max_pkt_cache,
195 			en_priv->state.rx_next);
196 
197 	return nb_out;
198 }
199 
200 int
rte_pdcp_entity_release(struct rte_pdcp_entity * pdcp_entity,struct rte_mbuf * out_mb[])201 rte_pdcp_entity_release(struct rte_pdcp_entity *pdcp_entity, struct rte_mbuf *out_mb[])
202 {
203 	struct entity_priv *en_priv;
204 	int nb_out = 0;
205 
206 	if (pdcp_entity == NULL)
207 		return -EINVAL;
208 
209 	en_priv = entity_priv_get(pdcp_entity);
210 
211 	if (!en_priv->flags.is_ul_entity)
212 		nb_out = pdcp_dl_release(pdcp_entity, out_mb);
213 
214 	/* Teardown crypto sessions */
215 	pdcp_crypto_sess_destroy(pdcp_entity);
216 
217 	rte_free(pdcp_entity);
218 
219 	return nb_out;
220 }
221 
222 int
rte_pdcp_entity_suspend(struct rte_pdcp_entity * pdcp_entity,struct rte_mbuf * out_mb[])223 rte_pdcp_entity_suspend(struct rte_pdcp_entity *pdcp_entity,
224 			struct rte_mbuf *out_mb[])
225 {
226 	struct entity_priv_dl_part *dl;
227 	struct entity_priv *en_priv;
228 	int nb_out = 0;
229 
230 	if (pdcp_entity == NULL)
231 		return -EINVAL;
232 
233 	en_priv = entity_priv_get(pdcp_entity);
234 
235 	if (en_priv->flags.is_ul_entity) {
236 		en_priv->state.tx_next = 0;
237 	} else {
238 		dl = entity_dl_part_get(pdcp_entity);
239 		nb_out = pdcp_reorder_up_to_get(&dl->reorder, out_mb, pdcp_entity->max_pkt_cache,
240 				en_priv->state.rx_next);
241 		pdcp_reorder_stop(&dl->reorder);
242 		en_priv->state.rx_next = 0;
243 		en_priv->state.rx_deliv = 0;
244 	}
245 
246 	return nb_out;
247 }
248 
249 struct rte_mbuf *
rte_pdcp_control_pdu_create(struct rte_pdcp_entity * pdcp_entity,enum rte_pdcp_ctrl_pdu_type type)250 rte_pdcp_control_pdu_create(struct rte_pdcp_entity *pdcp_entity,
251 			    enum rte_pdcp_ctrl_pdu_type type)
252 {
253 	struct entity_priv_dl_part *dl;
254 	struct entity_priv *en_priv;
255 	struct rte_mbuf *m;
256 	int ret;
257 
258 	if (pdcp_entity == NULL) {
259 		rte_errno = EINVAL;
260 		return NULL;
261 	}
262 
263 	en_priv = entity_priv_get(pdcp_entity);
264 	dl = entity_dl_part_get(pdcp_entity);
265 
266 	m = rte_pktmbuf_alloc(en_priv->ctrl_pdu_pool);
267 	if (m == NULL) {
268 		rte_errno = ENOMEM;
269 		return NULL;
270 	}
271 
272 	switch (type) {
273 	case RTE_PDCP_CTRL_PDU_TYPE_STATUS_REPORT:
274 		ret = pdcp_ctrl_pdu_status_gen(en_priv, dl, m);
275 		break;
276 	default:
277 		ret = -ENOTSUP;
278 	}
279 
280 	if (ret) {
281 		rte_pktmbuf_free(m);
282 		rte_errno = -ret;
283 		return NULL;
284 	}
285 
286 	return m;
287 }
288 
289 uint16_t
rte_pdcp_t_reordering_expiry_handle(const struct rte_pdcp_entity * entity,struct rte_mbuf * out_mb[])290 rte_pdcp_t_reordering_expiry_handle(const struct rte_pdcp_entity *entity, struct rte_mbuf *out_mb[])
291 {
292 	struct entity_priv_dl_part *dl = entity_dl_part_get(entity);
293 	struct entity_priv *en_priv = entity_priv_get(entity);
294 	uint16_t capacity = entity->max_pkt_cache;
295 	uint16_t nb_out, nb_seq;
296 
297 	/* 5.2.2.2 Actions when a t-Reordering expires */
298 
299 	/*
300 	 * - deliver to upper layers in ascending order of the associated COUNT value after
301 	 *   performing header decompression, if not decompressed before:
302 	 */
303 
304 	/*   - all stored PDCP SDU(s) with associated COUNT value(s) < RX_REORD; */
305 	nb_out = pdcp_reorder_up_to_get(&dl->reorder, out_mb, capacity, en_priv->state.rx_reord);
306 	capacity -= nb_out;
307 	out_mb = &out_mb[nb_out];
308 
309 	/*
310 	 *   - all stored PDCP SDU(s) with consecutively associated COUNT value(s) starting from
311 	 *     RX_REORD;
312 	 */
313 	nb_seq = pdcp_reorder_get_sequential(&dl->reorder, out_mb, capacity);
314 	nb_out += nb_seq;
315 
316 	/*
317 	 * - update RX_DELIV to the COUNT value of the first PDCP SDU which has not been delivered
318 	 *   to upper layers, with COUNT value >= RX_REORD;
319 	 */
320 	pdcp_rx_deliv_set(entity, en_priv->state.rx_reord + nb_seq);
321 
322 	/*
323 	 * - if RX_DELIV < RX_NEXT:
324 	 *   - update RX_REORD to RX_NEXT;
325 	 *   - start t-Reordering.
326 	 */
327 	if (en_priv->state.rx_deliv < en_priv->state.rx_next) {
328 		en_priv->state.rx_reord = en_priv->state.rx_next;
329 		dl->t_reorder.state = TIMER_RUNNING;
330 		dl->t_reorder.handle.start(dl->t_reorder.handle.timer, dl->t_reorder.handle.args);
331 	} else {
332 		dl->t_reorder.state = TIMER_EXPIRED;
333 	}
334 
335 	return nb_out;
336 }
337