net: Enable running without TX or RX threads

Set the default behaviour of the networking subsystem so that
no TX or RX threads are created. This will save RAM as there
is no need to allocate stack space for the RX/TX threads.
Also this will give small improvement to network packet latency
shown here:
* with 1 traffic class (1 TX and RX thread)

Avg TX net_pkt (42707) time 60 us	[0->22->15->22=59 us]
Avg RX net_pkt (42697) time 36 us	[0->10->3->12->7=32 us]

* with 0 traffic classes (no TX and RX threads)

Avg TX net_pkt (41608) time 42 us	[0->21->20=41 us]
Avg RX net_pkt (41593) time 31 us	[0->9->12->8=29 us]

In this qemu_x86 test run, 40k UDP packets was transferred between
echo-server and echo-client. In TX the speed increase was 30% and
in RX it was 14%.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2020-12-01 10:23:20 +02:00 committed by Jukka Rissanen
parent ccadbe2e7d
commit f5fb80750e
8 changed files with 108 additions and 21 deletions

View file

@ -144,9 +144,9 @@ int net_send_data(struct net_pkt *pkt);
#define NET_TC_COUNT NET_TC_RX_COUNT
#endif
#else /* CONFIG_NET_TC_TX_COUNT && CONFIG_NET_TC_RX_COUNT */
#define NET_TC_TX_COUNT 1
#define NET_TC_RX_COUNT 1
#define NET_TC_COUNT 1
#define NET_TC_TX_COUNT 0
#define NET_TC_RX_COUNT 0
#define NET_TC_COUNT 0
#endif /* CONFIG_NET_TC_TX_COUNT && CONFIG_NET_TC_RX_COUNT */
/* @endcond */

View file

@ -221,6 +221,18 @@ struct net_stats_rx_time {
net_stats_t count;
};
#if NET_TC_TX_COUNT == 0
#define NET_TC_TX_STATS_COUNT 1
#else
#define NET_TC_TX_STATS_COUNT NET_TC_TX_COUNT
#endif
#if NET_TC_RX_COUNT == 0
#define NET_TC_RX_STATS_COUNT 1
#else
#define NET_TC_RX_STATS_COUNT NET_TC_RX_COUNT
#endif
/**
* @brief Traffic class statistics
*/
@ -234,7 +246,7 @@ struct net_stats_tc {
net_stats_t pkts;
net_stats_t bytes;
uint8_t priority;
} sent[NET_TC_TX_COUNT];
} sent[NET_TC_TX_STATS_COUNT];
struct {
struct net_stats_rx_time rx_time;
@ -245,7 +257,7 @@ struct net_stats_tc {
net_stats_t pkts;
net_stats_t bytes;
uint8_t priority;
} recv[NET_TC_RX_COUNT];
} recv[NET_TC_RX_STATS_COUNT];
};

View file

@ -140,9 +140,9 @@ config NET_SHELL_DYN_CMD_COMPLETION
config NET_TC_TX_COUNT
int "How many Tx traffic classes to have for each network device"
default 1
range 1 NET_TC_NUM_PRIORITIES if NET_TC_NUM_PRIORITIES<=8
range 1 8
default 0
range 0 NET_TC_NUM_PRIORITIES if NET_TC_NUM_PRIORITIES<=8
range 0 8
help
Define how many Tx traffic classes (queues) the system should have
when sending a network packet. The network packet priority can then
@ -153,12 +153,14 @@ config NET_TC_TX_COUNT
The default value is 1 which means that all the network traffic is
handled equally. In this implementation, the higher traffic class
value corresponds to lower thread priority.
If you select 0 here, then it means that all the network traffic
is pushed to the driver directly without any queues.
config NET_TC_RX_COUNT
int "How many Rx traffic classes to have for each network device"
default 1
range 1 NET_TC_NUM_PRIORITIES if NET_TC_NUM_PRIORITIES<=8
range 1 8
range 0 NET_TC_NUM_PRIORITIES if NET_TC_NUM_PRIORITIES<=8
range 0 8
help
Define how many Rx traffic classes (queues) the system should have
when receiving a network packet. The network packet priority can then
@ -169,6 +171,14 @@ config NET_TC_RX_COUNT
The default value is 1 which means that all the network traffic is
handled equally. In this implementation, the higher traffic class
value corresponds to lower thread priority.
If you select 0 here, then it means that all the network traffic
is pushed from the driver to application thread without any
intermediate RX queue. There is always a receive socket queue between
device driver and application. Disabling RX thread means that the
network device driver, that is typically running in IRQ context, will
handle the packet all the way to the application. This might cause
other incoming packets to be lost if the RX processing takes long
time.
config NET_TC_SKIP_FOR_HIGH_PRIO
bool "Push high priority packets directly to network driver"

View file

@ -394,7 +394,11 @@ static void net_queue_rx(struct net_if *iface, struct net_pkt *pkt)
NET_DBG("TC %d with prio %d pkt %p", tc, prio, pkt);
#endif
net_tc_submit_to_rx_queue(tc, pkt);
if (NET_TC_RX_COUNT == 0) {
process_rx_packet(net_pkt_work(pkt));
} else {
net_tc_submit_to_rx_queue(tc, pkt);
}
}
/* Called by driver when an IP packet has been received */

View file

@ -346,10 +346,11 @@ void net_if_queue_tx(struct net_if *iface, struct net_pkt *pkt)
net_stats_update_tc_sent_priority(iface, tc, prio);
/* For highest priority packet, skip the TX queue and push directly to
* the driver.
* the driver. Also if there are no TX queue/thread, push the packet
* directly to the driver.
*/
if (IS_ENABLED(CONFIG_NET_TC_SKIP_FOR_HIGH_PRIO) &&
prio == NET_PRIORITY_CA) {
if ((IS_ENABLED(CONFIG_NET_TC_SKIP_FOR_HIGH_PRIO) &&
prio == NET_PRIORITY_CA) || NET_TC_TX_COUNT == 0) {
net_pkt_set_tx_stats_tick(pkt, k_cycle_get_32());
net_if_tx(net_pkt_iface(pkt), pkt);

View file

@ -942,7 +942,7 @@ static char *get_net_pkt_tc_stats_detail(struct net_if *iface, int i,
}
#endif /* (NET_TC_TX_COUNT > 1) || (NET_TC_RX_COUNT > 1) */
#if (NET_TC_TX_COUNT == 1) || (NET_TC_RX_COUNT == 1)
#if (NET_TC_TX_COUNT <= 1) || (NET_TC_RX_COUNT <= 1)
static char *get_net_pkt_stats_detail(struct net_if *iface, bool is_tx)
{
static char extra_stats[sizeof("\t[0=xxxx us]") + sizeof("->xxxx") *

View file

@ -32,43 +32,69 @@ K_KERNEL_STACK_ARRAY_DEFINE(tx_stack, NET_TC_TX_COUNT,
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
bool net_tc_submit_to_tx_queue(uint8_t tc, struct net_pkt *pkt)
{
#if NET_TC_TX_COUNT > 0
net_pkt_set_tx_stats_tick(pkt, k_cycle_get_32());
k_work_submit_to_queue(&tx_classes[tc].work_q, net_pkt_work(pkt));
#else
ARG_UNUSED(tc);
ARG_UNUSED(pkt);
#endif
return true;
}
void net_tc_submit_to_rx_queue(uint8_t tc, struct net_pkt *pkt)
{
#if NET_TC_RX_COUNT > 0
net_pkt_set_rx_stats_tick(pkt, k_cycle_get_32());
k_work_submit_to_queue(&rx_classes[tc].work_q, net_pkt_work(pkt));
#else
ARG_UNUSED(tc);
ARG_UNUSED(pkt);
#endif
}
int net_tx_priority2tc(enum net_priority prio)
{
#if NET_TC_TX_COUNT > 0
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
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
}
@ -88,6 +114,7 @@ int net_rx_priority2tc(enum net_priority prio)
#define PRIO_RX(i, _) (BASE_PRIO_RX - i),
#if NET_TC_TX_COUNT > 0
/* Convert traffic class to thread priority */
static uint8_t tx_tc2thread(uint8_t tc)
{
@ -134,7 +161,9 @@ static uint8_t tx_tc2thread(uint8_t tc)
return thread_priorities[tc];
}
#endif
#if NET_TC_RX_COUNT > 0
/* Convert traffic class to thread priority */
static uint8_t rx_tc2thread(uint8_t tc)
{
@ -149,11 +178,13 @@ static uint8_t rx_tc2thread(uint8_t tc)
return thread_priorities[tc];
}
#endif
#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;
@ -163,7 +194,9 @@ static void tc_tx_stats_priority_setup(struct net_if *iface)
i);
}
}
#endif
#if NET_TC_RX_COUNT > 0
static void tc_rx_stats_priority_setup(struct net_if *iface)
{
int i;
@ -173,7 +206,9 @@ static void tc_rx_stats_priority_setup(struct net_if *iface)
i);
}
}
#endif
#if NET_TC_TX_COUNT > 0
static void net_tc_tx_stats_priority_setup(struct net_if *iface,
void *user_data)
{
@ -181,7 +216,9 @@ static void net_tc_tx_stats_priority_setup(struct net_if *iface,
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)
{
@ -190,6 +227,7 @@ static void net_tc_rx_stats_priority_setup(struct net_if *iface,
tc_rx_stats_priority_setup(iface);
}
#endif
#endif
/* Create workqueue for each traffic class we are using. All the network
* traffic goes through these classes. There needs to be at least one traffic
@ -197,9 +235,13 @@ static void net_tc_rx_stats_priority_setup(struct net_if *iface,
*/
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);
BUILD_ASSERT(NET_TC_TX_COUNT >= 0);
#if defined(CONFIG_NET_STATISTICS)
net_if_foreach(net_tc_tx_stats_priority_setup, NULL);
@ -235,13 +277,18 @@ void net_tc_tx_init(void)
k_thread_name_set(&tx_classes[i].work_q.thread, name);
}
}
#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);
BUILD_ASSERT(NET_TC_RX_COUNT >= 0);
#if defined(CONFIG_NET_STATISTICS)
net_if_foreach(net_tc_rx_stats_priority_setup, NULL);
@ -277,4 +324,5 @@ void net_tc_rx_init(void)
k_thread_name_set(&rx_classes[i].work_q.thread, name);
}
}
#endif
}

View file

@ -33,7 +33,7 @@
#define PRIORITY2TC_GEN_INNER(TYPE, COUNT) priority2tc_ ## TYPE ## _ ## COUNT
#define PRIORITY2TC_GEN(TYPE, COUNT) PRIORITY2TC_GEN_INNER(TYPE, COUNT)
#if defined(CONFIG_NET_TC_MAPPING_STRICT)
#if defined(CONFIG_NET_TC_MAPPING_STRICT) && (NET_TC_COUNT > 0)
/* This is the recommended priority to traffic class mapping for
* implementations that do not support the credit-based shaper transmission
@ -66,10 +66,14 @@ static const uint8_t priority2tc_strict_7[] = {1, 0, 2, 3, 4, 4, 5, 6};
static const uint8_t priority2tc_strict_8[] = {1, 0, 2, 3, 4, 5, 6, 7};
#endif
#if NET_TC_TX_COUNT > 0
static const uint8_t *tx_prio2tc_map = PRIORITY2TC_GEN(strict, NET_TC_TX_COUNT);
#endif
#if NET_TC_RX_COUNT > 0
static const uint8_t *rx_prio2tc_map = PRIORITY2TC_GEN(strict, NET_TC_RX_COUNT);
#endif
#elif defined(CONFIG_NET_TC_MAPPING_SR_CLASS_A_AND_B)
#elif defined(CONFIG_NET_TC_MAPPING_SR_CLASS_A_AND_B) && (NET_TC_COUNT > 0)
/* This is the recommended priority to traffic class mapping for a system that
* supports SR (Stream Reservation) class A and SR class B.
@ -98,10 +102,14 @@ static const uint8_t priority2tc_sr_ab_7[] = {0, 0, 5, 6, 1, 2, 3, 4};
static const uint8_t priority2tc_sr_ab_8[] = {1, 0, 6, 7, 2, 3, 4, 5};
#endif
#if NET_TC_TX_COUNT > 0
static const uint8_t *tx_prio2tc_map = PRIORITY2TC_GEN(sr_ab, NET_TC_TX_COUNT);
#endif
#if NET_TC_RX_COUNT > 0
static const uint8_t *rx_prio2tc_map = PRIORITY2TC_GEN(sr_ab, NET_TC_RX_COUNT);
#endif
#elif defined(CONFIG_NET_TC_MAPPING_SR_CLASS_B_ONLY)
#elif defined(CONFIG_NET_TC_MAPPING_SR_CLASS_B_ONLY) && (NET_TC_COUNT > 0)
/* This is the recommended priority to traffic class mapping for a system that
* supports SR (Stream Reservation) class B only.
@ -130,8 +138,12 @@ static const uint8_t priority2tc_sr_b_7[] = {1, 0, 6, 2, 3, 3, 4, 5};
static const uint8_t priority2tc_sr_b_8[] = {1, 0, 7, 2, 3, 4, 5, 6};
#endif
#if NET_TC_TX_COUNT > 0
static const uint8_t *tx_prio2tc_map = PRIORITY2TC_GEN(sr_b, NET_TC_TX_COUNT);
#endif
#if NET_TC_RX_COUNT > 0
static const uint8_t *rx_prio2tc_map = PRIORITY2TC_GEN(sr_b, NET_TC_RX_COUNT);
#endif
#endif