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:
parent
c76aaf3818
commit
a5f9fc2382
2 changed files with 169 additions and 39 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue