drivers: ethernet: stm32: Updated original multicast hash filter change

- Applied changes on the most recent version of the driver
 - Using CONFIG_ETH_MULTICAST_FILTER to enable/disable the hash filter
 - Using read-modify-write the hash table for a single address
when joining
 - When leaving rebuild the entire hash table and ensure that multicast
addresses used for the hash calcuation doesn't have the joined flag set

I have tested these conditions:
 - IGMP enabled and disabled on my ethernet network
 - Observed the network utilisation on a STM32H7 device running these
changes with the hash filter enabled and disabled while the device is
on a ethernet network with high rate multicast traffic
 - When the application closes a socket for a multicast receive, ensure
it doesn't affect the receiption of existing sockets as well as IGMP

Fixes #53773

Signed-off-by: Chamira Perera <chamira.perera@audinate.com>
This commit is contained in:
Chamira Perera 2023-01-17 16:31:15 +11:00 committed by Carles Cufí
commit a5f9fc2382
2 changed files with 169 additions and 39 deletions

View file

@ -188,4 +188,15 @@ config ETH_STM32_HAL_PTP_CLOCK_INIT_PRIO
endif # SOC_SERIES_STM32F7X || SOC_SERIES_STM32H7X
config ETH_STM32_MULTICAST_FILTER
bool "Multicast hash filter support"
help
Enable support for multicast hash filtering in the MAC.
Once enabled the ethernet MAC performs imperfect filtering
based on a computed hash of the destination MAC address of
the multicast address. Only multicast with the computed
hash set in the multicast table will be received and all
other multicast is dropped by the MAC. If disabled then all
multicast is received by the MAC.
endif # ETH_STM32_HAL

View file

@ -17,7 +17,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <zephyr/device.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/util.h>
#include <zephyr/crc.h>
#include <zephyr/sys/crc.h>
#include <errno.h>
#include <stdbool.h>
#include <zephyr/net/net_pkt.h>
@ -97,8 +97,22 @@ static ETH_DMADescTypeDef dma_tx_desc_tab[ETH_TXBUFNB] __eth_stm32_desc;
static uint8_t dma_rx_buffer[ETH_RXBUFNB][ETH_STM32_RX_BUF_SIZE] __eth_stm32_buf;
static uint8_t dma_tx_buffer[ETH_TXBUFNB][ETH_STM32_TX_BUF_SIZE] __eth_stm32_buf;
#if defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
static struct net_if_mcast_monitor mcast_monitor;
static K_MUTEX_DEFINE(multicast_addr_lock);
#if defined(CONFIG_NET_NATIVE_IPV6)
static struct in6_addr multicast_ipv6_joined_addrs[NET_IF_MAX_IPV6_MADDR] = {0};
#endif /* CONFIG_NET_NATIVE_IPV6 */
#if defined(CONFIG_NET_NATIVE_IPV4)
static struct in_addr multicast_ipv4_joined_addrs[NET_IF_MAX_IPV4_MADDR] = {0};
#endif /* CONFIG_NET_NATIVE_IPV4 */
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
#if defined(CONFIG_ETH_STM32_HAL_API_V2)
BUILD_ASSERT(ETH_STM32_RX_BUF_SIZE % 4 == 0, "Rx buffer size must be a multiple of 4");
@ -217,7 +231,7 @@ static HAL_StatusTypeDef read_eth_phy_register(ETH_HandleTypeDef *heth,
#endif /* CONFIG_SOC_SERIES_STM32H7X || CONFIG_ETH_STM32_HAL_API_V2 */
}
static inline void disable_mcast_filter(ETH_HandleTypeDef *heth)
static inline void setup_mac_filter(ETH_HandleTypeDef *heth)
{
__ASSERT_NO_MSG(heth != NULL);
@ -225,8 +239,13 @@ static inline void disable_mcast_filter(ETH_HandleTypeDef *heth)
ETH_MACFilterConfigTypeDef MACFilterConf;
HAL_ETH_GetMACFilterConfig(heth, &MACFilterConf);
#if defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
MACFilterConf.HashMulticast = ENABLE;
MACFilterConf.PassAllMulticast = DISABLE;
#else
MACFilterConf.HashMulticast = DISABLE;
MACFilterConf.PassAllMulticast = ENABLE;
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
MACFilterConf.HachOrPerfectFilter = DISABLE;
HAL_ETH_SetMACFilterConfig(heth, &MACFilterConf);
@ -237,10 +256,15 @@ static inline void disable_mcast_filter(ETH_HandleTypeDef *heth)
/* disable multicast perfect filtering */
tmp &= ~(ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE |
#if !defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
ETH_MULTICASTFRAMESFILTER_HASHTABLE |
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
ETH_MULTICASTFRAMESFILTER_PERFECT);
#if defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
/* enable multicast hash receive filter */
tmp |= ETH_MULTICASTFRAMESFILTER_HASHTABLE;
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
heth->Instance->MACFFR = tmp;
@ -1189,6 +1213,64 @@ static int eth_initialize(const struct device *dev)
return 0;
}
#if defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
#if defined(CONFIG_NET_NATIVE_IPV6)
static void add_ipv6_multicast_addr(const struct in6_addr *addr)
{
uint32_t i;
for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) {
if (net_ipv6_is_addr_unspecified(&multicast_ipv6_joined_addrs[i])) {
net_ipv6_addr_copy_raw((uint8_t *)&multicast_ipv6_joined_addrs[i],
(uint8_t *)addr);
break;
}
}
}
static void remove_ipv6_multicast_addr(const struct in6_addr *addr)
{
uint32_t i;
for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) {
if (net_ipv6_addr_cmp_raw(&multicast_ipv6_joined_addrs[i], addr)) {
net_ipv6_addr_copy_raw((uint8_t *)&multicast_ipv6_joined_addrs[i],
(uint8_t *)net_ipv6_unspecified_address);
break;
}
}
}
#endif /* CONFIG_NET_NATIVE_IPV6 */
#if defined(CONFIG_NET_NATIVE_IPV4)
static void add_ipv4_multicast_addr(const struct in_addr *addr)
{
uint32_t i;
for (i = 0; i < NET_IF_MAX_IPV4_MADDR; i++) {
if (net_ipv4_is_addr_unspecified(&multicast_ipv4_joined_addrs[i])) {
net_ipv4_addr_copy_raw((uint8_t *)&multicast_ipv4_joined_addrs[i],
(uint8_t *)addr);
break;
}
}
}
static void remove_ipv4_multicast_addr(const struct in_addr *addr)
{
uint32_t i;
for (i = 0; i < NET_IF_MAX_IPV4_MADDR; i++) {
if (net_ipv4_addr_cmp_raw((uint8_t *)&multicast_ipv4_joined_addrs[i],
(uint8_t *)addr)) {
multicast_ipv4_joined_addrs[i].s_addr = 0;
break;
}
}
}
#endif /* CONFIG_NET_NATIVE_IPV4 */
static uint32_t reverse(uint32_t val)
{
uint32_t res = 0;
@ -1203,89 +1285,124 @@ static uint32_t reverse(uint32_t val)
return res;
}
static void net_if_mcast_cb(struct net_if *iface,
static void net_if_stm32_mcast_cb(struct net_if *iface,
const struct net_addr *addr,
bool is_joined)
{
ARG_UNUSED(addr);
ARG_UNUSED(is_joined);
const struct device *dev;
struct eth_stm32_hal_dev_data *dev_data;
ETH_HandleTypeDef *heth;
#if defined(CONFIG_NET_NATIVE_IPV6)
struct net_if_ipv6 *ipv6;
#endif
#if defined(CONFIG_NET_NATIVE_IPV4)
struct net_if_ipv4 *ipv4;
#endif
struct net_eth_addr mac_addr;
uint32_t crc;
uint32_t hash_table[2];
uint32_t hash_index;
int i;
__ASSERT_NO_MSG(iface != NULL);
dev = net_if_get_device(iface);
__ASSERT_NO_MSG(dev != NULL);
dev_data = DEV_DATA(dev);
__ASSERT_NO_MSG(dev_data != NULL);
dev_data = (struct eth_stm32_hal_dev_data *)dev->data;
heth = &dev_data->heth;
__ASSERT_NO_MSG(heth != NULL);
hash_table[0] = 0;
hash_table[1] = 0;
#if defined(CONFIG_NET_NATIVE_IPV6)
if (net_if_config_ipv6_get(iface, &ipv6) < 0) {
return;
if (is_joined) {
/* Save a copy of the hash table which we update with
* the hash for a single multicast address for join
*/
#if defined(CONFIG_SOC_SERIES_STM32H7X)
hash_table[0] = heth->Instance->MACHT0R;
hash_table[1] = heth->Instance->MACHT1R;
#else
hash_table[0] = heth->Instance->MACHTLR;
hash_table[1] = heth->Instance->MACHTHR;
#endif /* CONFIG_SOC_SERIES_STM32H7X */
}
k_mutex_lock(&multicast_addr_lock, K_FOREVER);
#if defined(CONFIG_NET_NATIVE_IPV6)
if (is_joined) {
/* When joining only update the hash filter with the joining
* multicast address.
*/
add_ipv6_multicast_addr(&addr->in6_addr);
net_eth_ipv6_mcast_to_mac_addr(&addr->in6_addr, &mac_addr);
crc = reverse(crc32_ieee(mac_addr.addr,
sizeof(struct net_eth_addr)));
hash_index = (crc >> 26) & 0x3f;
hash_table[hash_index / 32] |= (1 << (hash_index % 32));
} else {
/* When leaving its better to compute the full hash table
* for all the multicast addresses that we're aware of.
*/
remove_ipv6_multicast_addr(&addr->in6_addr);
for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) {
if (!ipv6->mcast[i].is_used) {
if (net_ipv6_is_addr_unspecified(&multicast_ipv6_joined_addrs[i])) {
continue;
}
net_eth_ipv6_mcast_to_mac_addr(&ipv6->mcast[i].address.in_addr,
net_eth_ipv6_mcast_to_mac_addr(&multicast_ipv6_joined_addrs[i],
&mac_addr);
crc = reverse(crc32_ieee(mac_addr.addr,
sizeof(struct net_eth_addr)));
hash_index = (crc >> 26) & 0x3f;
hash_table[hash_index / 32] |= (1 << (hash_index % 32));
}
}
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_NATIVE_IPV4)
if (net_if_config_ipv4_get(iface, &ipv4) < 0) {
return;
}
if (is_joined) {
/* When joining only update the hash filter with the joining
* multicast address.
*/
add_ipv4_multicast_addr(&addr->in_addr);
net_eth_ipv4_mcast_to_mac_addr(&addr->in_addr, &mac_addr);
crc = reverse(crc32_ieee(mac_addr.addr,
sizeof(struct net_eth_addr)));
hash_index = (crc >> 26) & 0x3f;
hash_table[hash_index / 32] |= (1 << (hash_index % 32));
} else {
/* When leaving its better to compute the full hash table
* for all the multicast addresses that we're aware of.
*/
remove_ipv4_multicast_addr(&addr->in_addr);
for (i = 0; i < NET_IF_MAX_IPV4_MADDR; i++) {
if (!ipv4->mcast[i].is_used) {
if (net_ipv4_is_addr_unspecified(&multicast_ipv4_joined_addrs[i])) {
continue;
}
net_eth_ipv4_mcast_to_mac_addr(&ipv4->mcast[i].address.in_addr,
net_eth_ipv4_mcast_to_mac_addr(&multicast_ipv4_joined_addrs[i],
&mac_addr);
crc = reverse(crc32_ieee(mac_addr.addr,
sizeof(struct net_eth_addr)));
hash_index = (crc >> 26) & 0x3f;
hash_table[hash_index / 32] |= (1 << (hash_index % 32));
}
}
#endif /* CONFIG_NET_IPV4 */
k_mutex_unlock(&multicast_addr_lock);
#if defined(CONFIG_SOC_SERIES_STM32H7X)
heth->Instance->MACHT0R = hash_table[0];
heth->Instance->MACHT1R = hash_table[1];
#else
heth->Instance->MACHTLR = hash_table[0];
heth->Instance->MACHTHR = hash_table[1];
#endif
#endif /* CONFIG_SOC_SERIES_STM32H7X */
}
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
static void eth_iface_init(struct net_if *iface)
{
const struct device *dev;
@ -1309,7 +1426,9 @@ static void eth_iface_init(struct net_if *iface)
is_first_init = true;
}
net_if_mcast_mon_register(&mcast_monitor, iface, net_if_mcast_cb);
#if defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
net_if_mcast_mon_register(&mcast_monitor, iface, net_if_stm32_mcast_cb);
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
/* Register Ethernet MAC Address with the upper layer */
net_if_set_link_addr(iface, dev_data->mac_addr,