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
|
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
|
endif # ETH_STM32_HAL
|
||||||
|
|
|
@ -17,7 +17,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||||
#include <zephyr/device.h>
|
#include <zephyr/device.h>
|
||||||
#include <zephyr/sys/__assert.h>
|
#include <zephyr/sys/__assert.h>
|
||||||
#include <zephyr/sys/util.h>
|
#include <zephyr/sys/util.h>
|
||||||
#include <zephyr/crc.h>
|
#include <zephyr/sys/crc.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <zephyr/net/net_pkt.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_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;
|
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 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)
|
#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");
|
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 */
|
#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);
|
__ASSERT_NO_MSG(heth != NULL);
|
||||||
|
|
||||||
|
@ -225,8 +239,13 @@ static inline void disable_mcast_filter(ETH_HandleTypeDef *heth)
|
||||||
ETH_MACFilterConfigTypeDef MACFilterConf;
|
ETH_MACFilterConfigTypeDef MACFilterConf;
|
||||||
|
|
||||||
HAL_ETH_GetMACFilterConfig(heth, &MACFilterConf);
|
HAL_ETH_GetMACFilterConfig(heth, &MACFilterConf);
|
||||||
|
#if defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
|
||||||
MACFilterConf.HashMulticast = ENABLE;
|
MACFilterConf.HashMulticast = ENABLE;
|
||||||
MACFilterConf.PassAllMulticast = DISABLE;
|
MACFilterConf.PassAllMulticast = DISABLE;
|
||||||
|
#else
|
||||||
|
MACFilterConf.HashMulticast = DISABLE;
|
||||||
|
MACFilterConf.PassAllMulticast = ENABLE;
|
||||||
|
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
|
||||||
MACFilterConf.HachOrPerfectFilter = DISABLE;
|
MACFilterConf.HachOrPerfectFilter = DISABLE;
|
||||||
|
|
||||||
HAL_ETH_SetMACFilterConfig(heth, &MACFilterConf);
|
HAL_ETH_SetMACFilterConfig(heth, &MACFilterConf);
|
||||||
|
@ -237,10 +256,15 @@ static inline void disable_mcast_filter(ETH_HandleTypeDef *heth)
|
||||||
|
|
||||||
/* disable multicast perfect filtering */
|
/* disable multicast perfect filtering */
|
||||||
tmp &= ~(ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE |
|
tmp &= ~(ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE |
|
||||||
|
#if !defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
|
||||||
|
ETH_MULTICASTFRAMESFILTER_HASHTABLE |
|
||||||
|
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
|
||||||
ETH_MULTICASTFRAMESFILTER_PERFECT);
|
ETH_MULTICASTFRAMESFILTER_PERFECT);
|
||||||
|
|
||||||
|
#if defined(CONFIG_ETH_STM32_MULTICAST_FILTER)
|
||||||
/* enable multicast hash receive filter */
|
/* enable multicast hash receive filter */
|
||||||
tmp |= ETH_MULTICASTFRAMESFILTER_HASHTABLE;
|
tmp |= ETH_MULTICASTFRAMESFILTER_HASHTABLE;
|
||||||
|
#endif /* CONFIG_ETH_STM32_MULTICAST_FILTER */
|
||||||
|
|
||||||
heth->Instance->MACFFR = tmp;
|
heth->Instance->MACFFR = tmp;
|
||||||
|
|
||||||
|
@ -1189,6 +1213,64 @@ static int eth_initialize(const struct device *dev)
|
||||||
return 0;
|
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)
|
static uint32_t reverse(uint32_t val)
|
||||||
{
|
{
|
||||||
uint32_t res = 0;
|
uint32_t res = 0;
|
||||||
|
@ -1203,89 +1285,124 @@ static uint32_t reverse(uint32_t val)
|
||||||
return res;
|
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,
|
const struct net_addr *addr,
|
||||||
bool is_joined)
|
bool is_joined)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(addr);
|
ARG_UNUSED(addr);
|
||||||
ARG_UNUSED(is_joined);
|
|
||||||
|
|
||||||
const struct device *dev;
|
const struct device *dev;
|
||||||
struct eth_stm32_hal_dev_data *dev_data;
|
struct eth_stm32_hal_dev_data *dev_data;
|
||||||
ETH_HandleTypeDef *heth;
|
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;
|
struct net_eth_addr mac_addr;
|
||||||
uint32_t crc;
|
uint32_t crc;
|
||||||
uint32_t hash_table[2];
|
uint32_t hash_table[2];
|
||||||
uint32_t hash_index;
|
uint32_t hash_index;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
__ASSERT_NO_MSG(iface != NULL);
|
|
||||||
|
|
||||||
dev = net_if_get_device(iface);
|
dev = net_if_get_device(iface);
|
||||||
__ASSERT_NO_MSG(dev != NULL);
|
|
||||||
|
|
||||||
dev_data = DEV_DATA(dev);
|
dev_data = (struct eth_stm32_hal_dev_data *)dev->data;
|
||||||
__ASSERT_NO_MSG(dev_data != NULL);
|
|
||||||
|
|
||||||
heth = &dev_data->heth;
|
heth = &dev_data->heth;
|
||||||
__ASSERT_NO_MSG(heth != NULL);
|
|
||||||
|
|
||||||
hash_table[0] = 0;
|
hash_table[0] = 0;
|
||||||
hash_table[1] = 0;
|
hash_table[1] = 0;
|
||||||
|
|
||||||
#if defined(CONFIG_NET_NATIVE_IPV6)
|
if (is_joined) {
|
||||||
if (net_if_config_ipv6_get(iface, &ipv6) < 0) {
|
/* Save a copy of the hash table which we update with
|
||||||
return;
|
* 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 */
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) {
|
k_mutex_lock(&multicast_addr_lock, K_FOREVER);
|
||||||
if (!ipv6->mcast[i].is_used) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
net_eth_ipv6_mcast_to_mac_addr(&ipv6->mcast[i].address.in_addr,
|
#if defined(CONFIG_NET_NATIVE_IPV6)
|
||||||
&mac_addr);
|
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,
|
crc = reverse(crc32_ieee(mac_addr.addr,
|
||||||
sizeof(struct net_eth_addr)));
|
sizeof(struct net_eth_addr)));
|
||||||
hash_index = (crc >> 26) & 0x3f;
|
hash_index = (crc >> 26) & 0x3f;
|
||||||
hash_table[hash_index / 32] |= (1 << (hash_index % 32));
|
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 (net_ipv6_is_addr_unspecified(&multicast_ipv6_joined_addrs[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
#endif /* CONFIG_NET_IPV6 */
|
||||||
|
|
||||||
#if defined(CONFIG_NET_NATIVE_IPV4)
|
#if defined(CONFIG_NET_NATIVE_IPV4)
|
||||||
if (net_if_config_ipv4_get(iface, &ipv4) < 0) {
|
if (is_joined) {
|
||||||
return;
|
/* When joining only update the hash filter with the joining
|
||||||
}
|
* multicast address.
|
||||||
|
*/
|
||||||
|
add_ipv4_multicast_addr(&addr->in_addr);
|
||||||
|
|
||||||
for (i = 0; i < NET_IF_MAX_IPV4_MADDR; i++) {
|
net_eth_ipv4_mcast_to_mac_addr(&addr->in_addr, &mac_addr);
|
||||||
if (!ipv4->mcast[i].is_used) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
net_eth_ipv4_mcast_to_mac_addr(&ipv4->mcast[i].address.in_addr,
|
|
||||||
&mac_addr);
|
|
||||||
crc = reverse(crc32_ieee(mac_addr.addr,
|
crc = reverse(crc32_ieee(mac_addr.addr,
|
||||||
sizeof(struct net_eth_addr)));
|
sizeof(struct net_eth_addr)));
|
||||||
hash_index = (crc >> 26) & 0x3f;
|
hash_index = (crc >> 26) & 0x3f;
|
||||||
hash_table[hash_index / 32] |= (1 << (hash_index % 32));
|
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 (net_ipv4_is_addr_unspecified(&multicast_ipv4_joined_addrs[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
#endif /* CONFIG_NET_IPV4 */
|
||||||
|
|
||||||
|
k_mutex_unlock(&multicast_addr_lock);
|
||||||
|
|
||||||
#if defined(CONFIG_SOC_SERIES_STM32H7X)
|
#if defined(CONFIG_SOC_SERIES_STM32H7X)
|
||||||
heth->Instance->MACHT0R = hash_table[0];
|
heth->Instance->MACHT0R = hash_table[0];
|
||||||
heth->Instance->MACHT1R = hash_table[1];
|
heth->Instance->MACHT1R = hash_table[1];
|
||||||
#else
|
#else
|
||||||
heth->Instance->MACHTLR = hash_table[0];
|
heth->Instance->MACHTLR = hash_table[0];
|
||||||
heth->Instance->MACHTHR = hash_table[1];
|
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)
|
static void eth_iface_init(struct net_if *iface)
|
||||||
{
|
{
|
||||||
const struct device *dev;
|
const struct device *dev;
|
||||||
|
@ -1309,7 +1426,9 @@ static void eth_iface_init(struct net_if *iface)
|
||||||
is_first_init = true;
|
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 */
|
/* Register Ethernet MAC Address with the upper layer */
|
||||||
net_if_set_link_addr(iface, dev_data->mac_addr,
|
net_if_set_link_addr(iface, dev_data->mac_addr,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue