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