1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_tc, CONFIG_NET_TC_LOG_LEVEL);
9 
10 #include <zephyr/kernel.h>
11 #include <string.h>
12 
13 #include <zephyr/net/net_core.h>
14 #include <zephyr/net/net_pkt.h>
15 #include <zephyr/net/net_stats.h>
16 
17 #include "net_private.h"
18 #include "net_stats.h"
19 #include "net_tc_mapping.h"
20 
21 /* Template for thread name. The "xx" is either "TX" denoting transmit thread,
22  * or "RX" denoting receive thread. The "q[y]" denotes the traffic class queue
23  * where y indicates the traffic class id. The value of y can be from 0 to 7.
24  */
25 #define MAX_NAME_LEN sizeof("xx_q[y]")
26 
27 /* Stacks for TX work queue */
28 K_KERNEL_STACK_ARRAY_DEFINE(tx_stack, NET_TC_TX_COUNT,
29 			    CONFIG_NET_TX_STACK_SIZE);
30 
31 /* Stacks for RX work queue */
32 K_KERNEL_STACK_ARRAY_DEFINE(rx_stack, NET_TC_RX_COUNT,
33 			    CONFIG_NET_RX_STACK_SIZE);
34 
35 #if NET_TC_TX_COUNT > 0
36 static struct net_traffic_class tx_classes[NET_TC_TX_COUNT];
37 #endif
38 
39 #if NET_TC_RX_COUNT > 0
40 static struct net_traffic_class rx_classes[NET_TC_RX_COUNT];
41 #endif
42 
43 #if NET_TC_RX_COUNT > 0 || NET_TC_TX_COUNT > 0
submit_to_queue(struct k_fifo * queue,struct net_pkt * pkt)44 static void submit_to_queue(struct k_fifo *queue, struct net_pkt *pkt)
45 {
46 	k_fifo_put(queue, pkt);
47 }
48 #endif
49 
net_tc_submit_to_tx_queue(uint8_t tc,struct net_pkt * pkt)50 bool net_tc_submit_to_tx_queue(uint8_t tc, struct net_pkt *pkt)
51 {
52 #if NET_TC_TX_COUNT > 0
53 	net_pkt_set_tx_stats_tick(pkt, k_cycle_get_32());
54 
55 	submit_to_queue(&tx_classes[tc].fifo, pkt);
56 #else
57 	ARG_UNUSED(tc);
58 	ARG_UNUSED(pkt);
59 #endif
60 	return true;
61 }
62 
net_tc_submit_to_rx_queue(uint8_t tc,struct net_pkt * pkt)63 void net_tc_submit_to_rx_queue(uint8_t tc, struct net_pkt *pkt)
64 {
65 #if NET_TC_RX_COUNT > 0
66 	net_pkt_set_rx_stats_tick(pkt, k_cycle_get_32());
67 
68 	submit_to_queue(&rx_classes[tc].fifo, pkt);
69 #else
70 	ARG_UNUSED(tc);
71 	ARG_UNUSED(pkt);
72 #endif
73 }
74 
net_tx_priority2tc(enum net_priority prio)75 int net_tx_priority2tc(enum net_priority prio)
76 {
77 #if NET_TC_TX_COUNT > 0
78 	if (prio > NET_PRIORITY_NC) {
79 		/* Use default value suggested in 802.1Q */
80 		prio = NET_PRIORITY_BE;
81 	}
82 
83 	return tx_prio2tc_map[prio];
84 #else
85 	ARG_UNUSED(prio);
86 
87 	return 0;
88 #endif
89 }
90 
net_rx_priority2tc(enum net_priority prio)91 int net_rx_priority2tc(enum net_priority prio)
92 {
93 #if NET_TC_RX_COUNT > 0
94 	if (prio > NET_PRIORITY_NC) {
95 		/* Use default value suggested in 802.1Q */
96 		prio = NET_PRIORITY_BE;
97 	}
98 
99 	return rx_prio2tc_map[prio];
100 #else
101 	ARG_UNUSED(prio);
102 
103 	return 0;
104 #endif
105 }
106 
107 #if defined(CONFIG_NET_TC_THREAD_PRIO_CUSTOM)
108 #define BASE_PRIO_TX CONFIG_NET_TC_TX_THREAD_BASE_PRIO
109 #elif defined(CONFIG_NET_TC_THREAD_COOPERATIVE)
110 #define BASE_PRIO_TX (CONFIG_NET_TC_NUM_PRIORITIES - 1)
111 #else
112 #define BASE_PRIO_TX (CONFIG_NET_TC_TX_COUNT - 1)
113 #endif
114 
115 #define PRIO_TX(i, _) (BASE_PRIO_TX - i)
116 
117 #if defined(CONFIG_NET_TC_THREAD_PRIO_CUSTOM)
118 #define BASE_PRIO_RX CONFIG_NET_TC_RX_THREAD_BASE_PRIO
119 #elif defined(CONFIG_NET_TC_THREAD_COOPERATIVE)
120 #define BASE_PRIO_RX (CONFIG_NET_TC_NUM_PRIORITIES - 1)
121 #else
122 #define BASE_PRIO_RX (CONFIG_NET_TC_RX_COUNT - 1)
123 #endif
124 
125 #define PRIO_RX(i, _) (BASE_PRIO_RX - i)
126 
127 #if NET_TC_TX_COUNT > 0
128 /* Convert traffic class to thread priority */
tx_tc2thread(uint8_t tc)129 static uint8_t tx_tc2thread(uint8_t tc)
130 {
131 	/* Initial implementation just maps the traffic class to certain queue.
132 	 * If there are less queues than classes, then map them into
133 	 * some specific queue.
134 	 *
135 	 * Lower value in this table means higher thread priority. The
136 	 * value is used as a parameter to K_PRIO_COOP() or K_PRIO_PREEMPT()
137 	 * which converts it to actual thread priority.
138 	 *
139 	 * Higher traffic class value means higher priority queue. This means
140 	 * that thread_priorities[7] value should contain the highest priority
141 	 * for the TX queue handling thread.
142 	 *
143 	 * For example, if NET_TC_TX_COUNT = 8, which is the maximum number of
144 	 * traffic classes, then this priority array will contain following
145 	 * values if preemptive priorities are used:
146 	 *      7, 6, 5, 4, 3, 2, 1, 0
147 	 * and
148 	 *      14, 13, 12, 11, 10, 9, 8, 7
149 	 * if cooperative priorities are used.
150 	 *
151 	 * Then these will be converted to following thread priorities if
152 	 * CONFIG_NET_TC_THREAD_COOPERATIVE is enabled:
153 	 *      -1, -2, -3, -4, -5, -6, -7, -8
154 	 *
155 	 * and if CONFIG_NET_TC_THREAD_PREEMPTIVE is enabled, following thread
156 	 * priorities are used:
157 	 *       7, 6, 5, 4, 3, 2, 1, 0
158 	 *
159 	 * This means that the lowest traffic class 1, will have the lowest
160 	 * cooperative priority -1 for coop priorities and 7 for preemptive
161 	 * priority.
162 	 */
163 	static const uint8_t thread_priorities[] = {
164 		LISTIFY(NET_TC_TX_COUNT, PRIO_TX, (,))
165 	};
166 
167 	BUILD_ASSERT(NET_TC_TX_COUNT <= CONFIG_NUM_COOP_PRIORITIES,
168 		     "Too many traffic classes");
169 
170 	NET_ASSERT(tc < ARRAY_SIZE(thread_priorities));
171 
172 	return thread_priorities[tc];
173 }
174 #endif
175 
176 #if NET_TC_RX_COUNT > 0
177 /* Convert traffic class to thread priority */
rx_tc2thread(uint8_t tc)178 static uint8_t rx_tc2thread(uint8_t tc)
179 {
180 	static const uint8_t thread_priorities[] = {
181 		LISTIFY(NET_TC_RX_COUNT, PRIO_RX, (,))
182 	};
183 
184 	BUILD_ASSERT(NET_TC_RX_COUNT <= CONFIG_NUM_COOP_PRIORITIES,
185 		     "Too many traffic classes");
186 
187 	NET_ASSERT(tc < ARRAY_SIZE(thread_priorities));
188 
189 	return thread_priorities[tc];
190 }
191 #endif
192 
193 #if defined(CONFIG_NET_STATISTICS)
194 /* Fixup the traffic class statistics so that "net stats" shell command will
195  * print output correctly.
196  */
197 #if NET_TC_TX_COUNT > 0
tc_tx_stats_priority_setup(struct net_if * iface)198 static void tc_tx_stats_priority_setup(struct net_if *iface)
199 {
200 	int i;
201 
202 	for (i = 0; i < 8; i++) {
203 		net_stats_update_tc_sent_priority(iface, net_tx_priority2tc(i),
204 						  i);
205 	}
206 }
207 #endif
208 
209 #if NET_TC_RX_COUNT > 0
tc_rx_stats_priority_setup(struct net_if * iface)210 static void tc_rx_stats_priority_setup(struct net_if *iface)
211 {
212 	int i;
213 
214 	for (i = 0; i < 8; i++) {
215 		net_stats_update_tc_recv_priority(iface, net_rx_priority2tc(i),
216 						  i);
217 	}
218 }
219 #endif
220 
221 #if NET_TC_TX_COUNT > 0
net_tc_tx_stats_priority_setup(struct net_if * iface,void * user_data)222 static void net_tc_tx_stats_priority_setup(struct net_if *iface,
223 					   void *user_data)
224 {
225 	ARG_UNUSED(user_data);
226 
227 	tc_tx_stats_priority_setup(iface);
228 }
229 #endif
230 
231 #if NET_TC_RX_COUNT > 0
net_tc_rx_stats_priority_setup(struct net_if * iface,void * user_data)232 static void net_tc_rx_stats_priority_setup(struct net_if *iface,
233 					   void *user_data)
234 {
235 	ARG_UNUSED(user_data);
236 
237 	tc_rx_stats_priority_setup(iface);
238 }
239 #endif
240 #endif
241 
242 #if NET_TC_RX_COUNT > 0
tc_rx_handler(void * p1,void * p2,void * p3)243 static void tc_rx_handler(void *p1, void *p2, void *p3)
244 {
245 	ARG_UNUSED(p2);
246 	ARG_UNUSED(p3);
247 
248 	struct k_fifo *fifo = p1;
249 	struct net_pkt *pkt;
250 
251 	while (1) {
252 		pkt = k_fifo_get(fifo, K_FOREVER);
253 		if (pkt == NULL) {
254 			continue;
255 		}
256 
257 		net_process_rx_packet(pkt);
258 	}
259 }
260 #endif
261 
262 #if NET_TC_TX_COUNT > 0
tc_tx_handler(void * p1,void * p2,void * p3)263 static void tc_tx_handler(void *p1, void *p2, void *p3)
264 {
265 	ARG_UNUSED(p2);
266 	ARG_UNUSED(p3);
267 
268 	struct k_fifo *fifo = p1;
269 	struct net_pkt *pkt;
270 
271 	while (1) {
272 		pkt = k_fifo_get(fifo, K_FOREVER);
273 		if (pkt == NULL) {
274 			continue;
275 		}
276 
277 		net_process_tx_packet(pkt);
278 	}
279 }
280 #endif
281 
282 /* Create a fifo for each traffic class we are using. All the network
283  * traffic goes through these classes.
284  */
net_tc_tx_init(void)285 void net_tc_tx_init(void)
286 {
287 #if NET_TC_TX_COUNT == 0
288 	NET_DBG("No %s thread created", "TX");
289 	return;
290 #else
291 	int i;
292 
293 	BUILD_ASSERT(NET_TC_TX_COUNT >= 0);
294 
295 #if defined(CONFIG_NET_STATISTICS)
296 	net_if_foreach(net_tc_tx_stats_priority_setup, NULL);
297 #endif
298 
299 	for (i = 0; i < NET_TC_TX_COUNT; i++) {
300 		uint8_t thread_priority;
301 		int priority;
302 		k_tid_t tid;
303 
304 		thread_priority = tx_tc2thread(i);
305 
306 		priority = IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE) ?
307 			K_PRIO_COOP(thread_priority) :
308 			K_PRIO_PREEMPT(thread_priority);
309 
310 		NET_DBG("[%d] Starting TX handler %p stack size %zd "
311 			"prio %d %s(%d)", i,
312 			&tx_classes[i].handler,
313 			K_KERNEL_STACK_SIZEOF(tx_stack[i]),
314 			thread_priority,
315 			IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE) ?
316 							"coop" : "preempt",
317 			priority);
318 
319 		k_fifo_init(&tx_classes[i].fifo);
320 
321 		tid = k_thread_create(&tx_classes[i].handler, tx_stack[i],
322 				      K_KERNEL_STACK_SIZEOF(tx_stack[i]),
323 				      tc_tx_handler,
324 				      &tx_classes[i].fifo, NULL, NULL,
325 				      priority, 0, K_FOREVER);
326 		if (!tid) {
327 			NET_ERR("Cannot create TC handler thread %d", i);
328 			continue;
329 		}
330 
331 		if (IS_ENABLED(CONFIG_THREAD_NAME)) {
332 			char name[MAX_NAME_LEN];
333 
334 			snprintk(name, sizeof(name), "tx_q[%d]", i);
335 			k_thread_name_set(tid, name);
336 		}
337 
338 		k_thread_start(tid);
339 	}
340 #endif
341 }
342 
net_tc_rx_init(void)343 void net_tc_rx_init(void)
344 {
345 #if NET_TC_RX_COUNT == 0
346 	NET_DBG("No %s thread created", "RX");
347 	return;
348 #else
349 	int i;
350 
351 	BUILD_ASSERT(NET_TC_RX_COUNT >= 0);
352 
353 #if defined(CONFIG_NET_STATISTICS)
354 	net_if_foreach(net_tc_rx_stats_priority_setup, NULL);
355 #endif
356 
357 	for (i = 0; i < NET_TC_RX_COUNT; i++) {
358 		uint8_t thread_priority;
359 		int priority;
360 		k_tid_t tid;
361 
362 		thread_priority = rx_tc2thread(i);
363 
364 		priority = IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE) ?
365 			K_PRIO_COOP(thread_priority) :
366 			K_PRIO_PREEMPT(thread_priority);
367 
368 		NET_DBG("[%d] Starting RX handler %p stack size %zd "
369 			"prio %d %s(%d)", i,
370 			&rx_classes[i].handler,
371 			K_KERNEL_STACK_SIZEOF(rx_stack[i]),
372 			thread_priority,
373 			IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE) ?
374 							"coop" : "preempt",
375 			priority);
376 
377 		k_fifo_init(&rx_classes[i].fifo);
378 
379 		tid = k_thread_create(&rx_classes[i].handler, rx_stack[i],
380 				      K_KERNEL_STACK_SIZEOF(rx_stack[i]),
381 				      tc_rx_handler,
382 				      &rx_classes[i].fifo, NULL, NULL,
383 				      priority, 0, K_FOREVER);
384 		if (!tid) {
385 			NET_ERR("Cannot create TC handler thread %d", i);
386 			continue;
387 		}
388 
389 		if (IS_ENABLED(CONFIG_THREAD_NAME)) {
390 			char name[MAX_NAME_LEN];
391 
392 			snprintk(name, sizeof(name), "rx_q[%d]", i);
393 			k_thread_name_set(tid, name);
394 		}
395 
396 		k_thread_start(tid);
397 	}
398 #endif
399 }
400