blob: fad0150563aec21c20c1878d87cdd2111dea304b [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_tc, CONFIG_NET_TC_LOG_LEVEL);
#include <zephyr/kernel.h>
#include <string.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_stats.h>
#include "net_private.h"
#include "net_stats.h"
#include "net_tc_mapping.h"
#if NET_TC_RX_EFFECTIVE_COUNT > 1
#define NET_TC_RX_SLOTS (CONFIG_NET_PKT_RX_COUNT / NET_TC_RX_EFFECTIVE_COUNT)
BUILD_ASSERT(NET_TC_RX_SLOTS > 0,
"Misconfiguration: There are more traffic classes then packets, "
"either increase CONFIG_NET_PKT_RX_COUNT or decrease "
"CONFIG_NET_TC_RX_COUNT or disable CONFIG_NET_TC_RX_SKIP_FOR_HIGH_PRIO");
#endif
#if NET_TC_TX_EFFECTIVE_COUNT > 1
#define NET_TC_TX_SLOTS (CONFIG_NET_PKT_TX_COUNT / NET_TC_TX_EFFECTIVE_COUNT)
BUILD_ASSERT(NET_TC_TX_SLOTS > 0,
"Misconfiguration: There are more traffic classes then packets, "
"either increase CONFIG_NET_PKT_TX_COUNT or decrease "
"CONFIG_NET_TC_TX_COUNT or disable CONFIG_NET_TC_TX_SKIP_FOR_HIGH_PRIO");
#endif
#if NET_TC_RX_EFFECTIVE_COUNT > 1
#define NET_TC_RETRY_CNT 1
#endif
/* Template for thread name. The "xx" is either "TX" denoting transmit thread,
* or "RX" denoting receive thread. The "q[y]" denotes the traffic class queue
* where y indicates the traffic class id. The value of y can be from 0 to 7.
*/
#define MAX_NAME_LEN sizeof("xx_q[y]")
/* Stacks for TX work queue */
K_KERNEL_STACK_ARRAY_DEFINE(tx_stack, NET_TC_TX_COUNT,
CONFIG_NET_TX_STACK_SIZE);
/* Stacks for RX work queue */
K_KERNEL_STACK_ARRAY_DEFINE(rx_stack, NET_TC_RX_COUNT,
CONFIG_NET_RX_STACK_SIZE);
#if NET_TC_TX_COUNT > 0
static struct net_traffic_class tx_classes[NET_TC_TX_COUNT];
#endif
#if NET_TC_RX_COUNT > 0
static struct net_traffic_class rx_classes[NET_TC_RX_COUNT];
#endif
enum net_verdict net_tc_try_submit_to_tx_queue(uint8_t tc, struct net_pkt *pkt,
k_timeout_t timeout)
{
#if NET_TC_TX_COUNT > 0
net_pkt_set_tx_stats_tick(pkt, k_cycle_get_32());
#if NET_TC_TX_EFFECTIVE_COUNT > 1
if (k_sem_take(&tx_classes[tc].fifo_slot, timeout) != 0) {
return NET_DROP;
}
#endif
k_fifo_put(&tx_classes[tc].fifo, pkt);
return NET_OK;
#else
ARG_UNUSED(tc);
ARG_UNUSED(pkt);
return NET_DROP;
#endif
}
enum net_verdict net_tc_submit_to_rx_queue(uint8_t tc, struct net_pkt *pkt)
{
#if NET_TC_RX_COUNT > 0
#if NET_TC_RX_EFFECTIVE_COUNT > 1
uint8_t retry_cnt = NET_TC_RETRY_CNT;
#endif
net_pkt_set_rx_stats_tick(pkt, k_cycle_get_32());
#if NET_TC_RX_EFFECTIVE_COUNT > 1
while (k_sem_take(&rx_classes[tc].fifo_slot, K_NO_WAIT) != 0) {
if (k_is_in_isr() || retry_cnt == 0) {
return NET_DROP;
}
retry_cnt--;
/* Let thread with same priority run,
* try to reduce dropping packets
*/
k_yield();
}
#endif
k_fifo_put(&rx_classes[tc].fifo, pkt);
return NET_OK;
#else
ARG_UNUSED(tc);
ARG_UNUSED(pkt);
return NET_DROP;
#endif
}
int net_tx_priority2tc(enum net_priority prio)
{
#if NET_TC_TX_COUNT > 0
static const uint8_t tx_prio2tc_map[] = PRIORITY2TC_TX;
if (prio > NET_PRIORITY_NC) {
/* Use default value suggested in 802.1Q */
prio = NET_PRIORITY_BE;
}
return tx_prio2tc_map[prio];
#else
ARG_UNUSED(prio);
return 0;
#endif
}
int net_rx_priority2tc(enum net_priority prio)
{
#if NET_TC_RX_COUNT > 0
static const uint8_t rx_prio2tc_map[] = PRIORITY2TC_RX;
if (prio > NET_PRIORITY_NC) {
/* Use default value suggested in 802.1Q */
prio = NET_PRIORITY_BE;
}
return rx_prio2tc_map[prio];
#else
ARG_UNUSED(prio);
return 0;
#endif
}
#if defined(CONFIG_NET_TC_THREAD_PRIO_CUSTOM)
#define BASE_PRIO_TX CONFIG_NET_TC_TX_THREAD_BASE_PRIO
#define PRIO_SPREAD_TX CONFIG_NET_TC_TX_THREAD_PRIO_SPREAD
#elif defined(CONFIG_NET_TC_THREAD_COOPERATIVE)
#define BASE_PRIO_TX (CONFIG_NET_TC_NUM_PRIORITIES - 1)
#define PRIO_SPREAD_TX 1
BUILD_ASSERT(NET_TC_TX_COUNT <= CONFIG_NUM_COOP_PRIORITIES, "Too many traffic classes");
#else
#define BASE_PRIO_TX (CONFIG_NET_TC_TX_COUNT - 1)
#define PRIO_SPREAD_TX 1
#endif
#if defined(CONFIG_NET_TC_THREAD_PRIO_CUSTOM)
#define BASE_PRIO_RX CONFIG_NET_TC_RX_THREAD_BASE_PRIO
#define PRIO_SPREAD_RX CONFIG_NET_TC_RX_THREAD_PRIO_SPREAD
#elif defined(CONFIG_NET_TC_THREAD_COOPERATIVE)
#define BASE_PRIO_RX (CONFIG_NET_TC_NUM_PRIORITIES - 1)
#define PRIO_SPREAD_RX 1
BUILD_ASSERT(NET_TC_RX_COUNT <= CONFIG_NUM_COOP_PRIORITIES, "Too many traffic classes");
#else
#define BASE_PRIO_RX (CONFIG_NET_TC_RX_COUNT - 1)
#define PRIO_SPREAD_RX 1
#endif
int net_tc_tx_thread_priority(int tc)
{
int priority;
int thread_priority;
BUILD_ASSERT(BASE_PRIO_TX >= PRIO_SPREAD_TX * (NET_TC_TX_COUNT - 1));
NET_ASSERT(tc >= 0 && tc < NET_TC_TX_COUNT);
thread_priority = BASE_PRIO_TX - PRIO_SPREAD_TX * tc;
priority = IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE) ?
K_PRIO_COOP(thread_priority) :
K_PRIO_PREEMPT(thread_priority);
return priority;
}
int net_tc_rx_thread_priority(int tc)
{
int priority;
int thread_priority;
BUILD_ASSERT(BASE_PRIO_RX >= PRIO_SPREAD_RX * (NET_TC_RX_COUNT - 1));
NET_ASSERT(tc >= 0 && tc < NET_TC_RX_COUNT);
thread_priority = BASE_PRIO_RX - PRIO_SPREAD_RX * tc;
priority = IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE) ?
K_PRIO_COOP(thread_priority) :
K_PRIO_PREEMPT(thread_priority);
return priority;
}
#if defined(CONFIG_NET_STATISTICS)
/* Fixup the traffic class statistics so that "net stats" shell command will
* print output correctly.
*/
#if NET_TC_TX_COUNT > 0
static void tc_tx_stats_priority_setup(struct net_if *iface)
{
int i;
for (i = 0; i < 8; i++) {
net_stats_update_tc_sent_priority(iface, net_tx_priority2tc(i),
i);
}
}
#endif
#if NET_TC_RX_COUNT > 0
static void tc_rx_stats_priority_setup(struct net_if *iface)
{
int i;
for (i = 0; i < 8; i++) {
net_stats_update_tc_recv_priority(iface, net_rx_priority2tc(i),
i);
}
}
#endif
#if NET_TC_TX_COUNT > 0
static void net_tc_tx_stats_priority_setup(struct net_if *iface,
void *user_data)
{
ARG_UNUSED(user_data);
tc_tx_stats_priority_setup(iface);
}
#endif
#if NET_TC_RX_COUNT > 0
static void net_tc_rx_stats_priority_setup(struct net_if *iface,
void *user_data)
{
ARG_UNUSED(user_data);
tc_rx_stats_priority_setup(iface);
}
#endif
#endif
#if NET_TC_RX_COUNT > 0
static void tc_rx_handler(void *p1, void *p2, void *p3)
{
ARG_UNUSED(p3);
struct k_fifo *fifo = p1;
#if NET_TC_RX_EFFECTIVE_COUNT > 1
struct k_sem *fifo_slot = p2;
#else
ARG_UNUSED(p2);
#endif
struct net_pkt *pkt;
while (1) {
pkt = k_fifo_get(fifo, K_FOREVER);
if (pkt == NULL) {
continue;
}
#if NET_TC_RX_EFFECTIVE_COUNT > 1
k_sem_give(fifo_slot);
#endif
net_process_rx_packet(pkt);
}
}
#endif
#if NET_TC_TX_COUNT > 0
static void tc_tx_handler(void *p1, void *p2, void *p3)
{
ARG_UNUSED(p3);
struct k_fifo *fifo = p1;
#if NET_TC_TX_EFFECTIVE_COUNT > 1
struct k_sem *fifo_slot = p2;
#else
ARG_UNUSED(p2);
#endif
struct net_pkt *pkt;
while (1) {
pkt = k_fifo_get(fifo, K_FOREVER);
if (pkt == NULL) {
continue;
}
#if NET_TC_TX_EFFECTIVE_COUNT > 1
k_sem_give(fifo_slot);
#endif
net_process_tx_packet(pkt);
}
}
#endif
/* Create a fifo for each traffic class we are using. All the network
* traffic goes through these classes.
*/
void net_tc_tx_init(void)
{
#if NET_TC_TX_COUNT == 0
NET_DBG("No %s thread created", "TX");
return;
#else
int i;
BUILD_ASSERT(NET_TC_TX_COUNT >= 0);
#if defined(CONFIG_NET_STATISTICS)
net_if_foreach(net_tc_tx_stats_priority_setup, NULL);
#endif
for (i = 0; i < NET_TC_TX_COUNT; i++) {
k_tid_t tid;
int priority = net_tc_tx_thread_priority(i);
NET_DBG("[%d] Starting TX handler %p stack size %zd prio %d", i,
&tx_classes[i].handler,
K_KERNEL_STACK_SIZEOF(tx_stack[i]),
priority);
k_fifo_init(&tx_classes[i].fifo);
#if NET_TC_TX_EFFECTIVE_COUNT > 1
k_sem_init(&tx_classes[i].fifo_slot, NET_TC_TX_SLOTS, NET_TC_TX_SLOTS);
#endif
tid = k_thread_create(&tx_classes[i].handler, tx_stack[i],
K_KERNEL_STACK_SIZEOF(tx_stack[i]),
tc_tx_handler,
&tx_classes[i].fifo,
#if NET_TC_TX_EFFECTIVE_COUNT > 1
&tx_classes[i].fifo_slot,
#else
NULL,
#endif
NULL,
priority, 0, K_FOREVER);
if (!tid) {
NET_ERR("Cannot create TC handler thread %d", i);
continue;
}
if (IS_ENABLED(CONFIG_THREAD_NAME)) {
char name[MAX_NAME_LEN];
snprintk(name, sizeof(name), "tx_q[%d]", i);
k_thread_name_set(tid, name);
}
k_thread_start(tid);
}
#endif
}
void net_tc_rx_init(void)
{
#if NET_TC_RX_COUNT == 0
NET_DBG("No %s thread created", "RX");
return;
#else
int i;
BUILD_ASSERT(NET_TC_RX_COUNT >= 0);
#if defined(CONFIG_NET_STATISTICS)
net_if_foreach(net_tc_rx_stats_priority_setup, NULL);
#endif
for (i = 0; i < NET_TC_RX_COUNT; i++) {
k_tid_t tid;
int priority = net_tc_rx_thread_priority(i);
NET_DBG("[%d] Starting RX handler %p stack size %zd prio %d", i,
&rx_classes[i].handler,
K_KERNEL_STACK_SIZEOF(rx_stack[i]),
priority);
k_fifo_init(&rx_classes[i].fifo);
#if NET_TC_RX_EFFECTIVE_COUNT > 1
k_sem_init(&rx_classes[i].fifo_slot, NET_TC_RX_SLOTS, NET_TC_RX_SLOTS);
#endif
tid = k_thread_create(&rx_classes[i].handler, rx_stack[i],
K_KERNEL_STACK_SIZEOF(rx_stack[i]),
tc_rx_handler,
&rx_classes[i].fifo,
#if NET_TC_RX_EFFECTIVE_COUNT > 1
&rx_classes[i].fifo_slot,
#else
NULL,
#endif
NULL,
priority, 0, K_FOREVER);
if (!tid) {
NET_ERR("Cannot create TC handler thread %d", i);
continue;
}
if (IS_ENABLED(CONFIG_THREAD_NAME)) {
char name[MAX_NAME_LEN];
snprintk(name, sizeof(name), "rx_q[%d]", i);
k_thread_name_set(tid, name);
}
k_thread_start(tid);
}
#endif
}