Skip to content

Commit

Permalink
Merge branch 'net-improve-multicast-group-join-performance'
Browse files Browse the repository at this point in the history
Jonas Rebmann says:

====================
improve multicast join group performance

This series seeks to improve performance on updating igmp group
memberships such as with IP_ADD_MEMBERSHIP or MCAST_JOIN_SOURCE_GROUP.

Our use case was to add 2000 multicast memberships on a TQMLS1046A which
took about 3.6 seconds for the membership additions alone. Our userspace
reproducer tool was instrumented to log runtimes of the individual
setsockopt invocations which clearly indicated quadratic complexity of
setting up the membership with regard to the total number of multicast
groups to be joined. We used perf to locate the hotspots and
subsequently optimized the most costly sections of code.

This series includes a patch to Linux igmp handling as well as a patch
to the DPAA/Freescale driver. With both patches applied, our memberships can
be set up in only about 87 miliseconds, which corresponds to a speedup
of around 40.

While we have acheived practically linear run-time complexity on the
kernel side, a small quadratic factor remains in parts of the freescale
driver code which we haven't yet optimized. We have by now payed little
attention to the optimization potential in dropping group memberships,
yet the dpaa patch applies to joining and leaving groups alike.

Overall, this patch series brings great improvements in use cases
involving large numbers of multicast groups, particularly when using the
fsl_dpa driver, without noteworthy drawbacks in other scenarios.
====================

Signed-off-by: Jonas Rebmann <jre@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
davem330 committed Oct 9, 2024
2 parents 2050327 + 298f70b commit 2a80d89
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 54 deletions.
20 changes: 18 additions & 2 deletions drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,22 @@ static int dpaa_set_mac_address(struct net_device *net_dev, void *addr)
return 0;
}

static int dpaa_addr_sync(struct net_device *net_dev, const u8 *addr)
{
const struct dpaa_priv *priv = netdev_priv(net_dev);

return priv->mac_dev->add_hash_mac_addr(priv->mac_dev->fman_mac,
(enet_addr_t *)addr);
}

static int dpaa_addr_unsync(struct net_device *net_dev, const u8 *addr)
{
const struct dpaa_priv *priv = netdev_priv(net_dev);

return priv->mac_dev->remove_hash_mac_addr(priv->mac_dev->fman_mac,
(enet_addr_t *)addr);
}

static void dpaa_set_rx_mode(struct net_device *net_dev)
{
const struct dpaa_priv *priv;
Expand Down Expand Up @@ -490,9 +506,9 @@ static void dpaa_set_rx_mode(struct net_device *net_dev)
err);
}

err = priv->mac_dev->set_multi(net_dev, priv->mac_dev);
err = __dev_mc_sync(net_dev, dpaa_addr_sync, dpaa_addr_unsync);
if (err < 0)
netif_err(priv, drv, net_dev, "mac_dev->set_multi() = %d\n",
netif_err(priv, drv, net_dev, "dpaa_addr_sync() = %d\n",
err);
}

Expand Down
1 change: 0 additions & 1 deletion drivers/net/ethernet/freescale/fman/fman_dtsec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1415,7 +1415,6 @@ int dtsec_initialization(struct mac_device *mac_dev,
mac_dev->set_exception = dtsec_set_exception;
mac_dev->set_allmulti = dtsec_set_allmulti;
mac_dev->set_tstamp = dtsec_set_tstamp;
mac_dev->set_multi = fman_set_multi;
mac_dev->enable = dtsec_enable;
mac_dev->disable = dtsec_disable;

Expand Down
1 change: 0 additions & 1 deletion drivers/net/ethernet/freescale/fman/fman_memac.c
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,6 @@ int memac_initialization(struct mac_device *mac_dev,
mac_dev->set_exception = memac_set_exception;
mac_dev->set_allmulti = memac_set_allmulti;
mac_dev->set_tstamp = memac_set_tstamp;
mac_dev->set_multi = fman_set_multi;
mac_dev->enable = memac_enable;
mac_dev->disable = memac_disable;

Expand Down
1 change: 0 additions & 1 deletion drivers/net/ethernet/freescale/fman/fman_tgec.c
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,6 @@ int tgec_initialization(struct mac_device *mac_dev,
mac_dev->set_exception = tgec_set_exception;
mac_dev->set_allmulti = tgec_set_allmulti;
mac_dev->set_tstamp = tgec_set_tstamp;
mac_dev->set_multi = fman_set_multi;
mac_dev->enable = tgec_enable;
mac_dev->disable = tgec_disable;

Expand Down
42 changes: 0 additions & 42 deletions drivers/net/ethernet/freescale/fman/mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ MODULE_DESCRIPTION("FSL FMan MAC API based driver");
struct mac_priv_s {
u8 cell_index;
struct fman *fman;
/* List of multicast addresses */
struct list_head mc_addr_list;
struct platform_device *eth_dev;
u16 speed;
};
Expand All @@ -57,44 +55,6 @@ static void mac_exception(struct mac_device *mac_dev,
__func__, ex);
}

int fman_set_multi(struct net_device *net_dev, struct mac_device *mac_dev)
{
struct mac_priv_s *priv;
struct mac_address *old_addr, *tmp;
struct netdev_hw_addr *ha;
int err;
enet_addr_t *addr;

priv = mac_dev->priv;

/* Clear previous address list */
list_for_each_entry_safe(old_addr, tmp, &priv->mc_addr_list, list) {
addr = (enet_addr_t *)old_addr->addr;
err = mac_dev->remove_hash_mac_addr(mac_dev->fman_mac, addr);
if (err < 0)
return err;

list_del(&old_addr->list);
kfree(old_addr);
}

/* Add all the addresses from the new list */
netdev_for_each_mc_addr(ha, net_dev) {
addr = (enet_addr_t *)ha->addr;
err = mac_dev->add_hash_mac_addr(mac_dev->fman_mac, addr);
if (err < 0)
return err;

tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC);
if (!tmp)
return -ENOMEM;

ether_addr_copy(tmp->addr, ha->addr);
list_add(&tmp->list, &priv->mc_addr_list);
}
return 0;
}

static DEFINE_MUTEX(eth_lock);

static struct platform_device *dpaa_eth_add_device(int fman_id,
Expand Down Expand Up @@ -181,8 +141,6 @@ static int mac_probe(struct platform_device *_of_dev)
mac_dev->priv = priv;
mac_dev->dev = dev;

INIT_LIST_HEAD(&priv->mc_addr_list);

/* Get the FM node */
dev_node = of_get_parent(mac_node);
if (!dev_node) {
Expand Down
2 changes: 0 additions & 2 deletions drivers/net/ethernet/freescale/fman/mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ struct mac_device {
int (*change_addr)(struct fman_mac *mac_dev, const enet_addr_t *enet_addr);
int (*set_allmulti)(struct fman_mac *mac_dev, bool enable);
int (*set_tstamp)(struct fman_mac *mac_dev, bool enable);
int (*set_multi)(struct net_device *net_dev,
struct mac_device *mac_dev);
int (*set_exception)(struct fman_mac *mac_dev,
enum fman_mac_exceptions exception, bool enable);
int (*add_hash_mac_addr)(struct fman_mac *mac_dev,
Expand Down
26 changes: 21 additions & 5 deletions net/ipv4/igmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1437,16 +1437,32 @@ static void ip_mc_hash_remove(struct in_device *in_dev,
static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
unsigned int mode, gfp_t gfp)
{
struct ip_mc_list __rcu **mc_hash;
struct ip_mc_list *im;

ASSERT_RTNL();

for_each_pmc_rtnl(in_dev, im) {
if (im->multiaddr == addr) {
im->users++;
ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0);
goto out;
mc_hash = rtnl_dereference(in_dev->mc_hash);
if (mc_hash) {
u32 hash = hash_32((__force u32)addr, MC_HASH_SZ_LOG);

for (im = rtnl_dereference(mc_hash[hash]);
im;
im = rtnl_dereference(im->next_hash)) {
if (im->multiaddr == addr)
break;
}
} else {
for_each_pmc_rtnl(in_dev, im) {
if (im->multiaddr == addr)
break;
}
}

if (im) {
im->users++;
ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0);
goto out;
}

im = kzalloc(sizeof(*im), gfp);
Expand Down

0 comments on commit 2a80d89

Please sign in to comment.