[PATCH] Re: dev_mc_lock question.

From: Gleb Natapov (gleb@nbase.co.il)
Date: Thu Aug 24 2000 - 08:58:28 EST


Hi,

kuznet@ms2.inr.ac.ru wrote:
>
[...]
>
> Yes, I want patch. Also, could you look into drivers/net/bonding.c
> and kill that shit, which is made in its set_multicast_list()?
>

 The attached patch fixes set_multicast_list() in bonding device. My
previous patch (that removes dev_mc_lock) should be applied before this
one, otherwise you'll get deadlock on smp.

--
			Gleb.

diff -urN -X exclude 2.4.0-test7-pre7/drivers/net/bonding.c 2.4.0-test7-pre7-bond/drivers/net/bonding.c --- 2.4.0-test7-pre7/drivers/net/bonding.c Thu Jul 6 08:15:27 2000 +++ 2.4.0-test7-pre7-bond/drivers/net/bonding.c Thu Aug 24 16:19:13 2000 @@ -28,7 +28,10 @@ * * v0.1 - first working version. * v0.2 - changed stats to be calculated by summing slaves stats. - * + * + * Fixes: + * Gleb Natapov : multicast handling fixed, + * emulation of promiscuity & allmulti added */ #include <linux/kernel.h> @@ -71,6 +74,8 @@ slave_t *current_slave; struct net_device_stats stats; + struct dev_mc_list *mc_list; + unsigned short flags; } bonding_t; @@ -94,6 +99,18 @@ bond->current_slave = slave->next; slave->next->prev = slave->prev; slave->prev->next = slave->next; + + /* flush master's mc_list from slave */ + dev_mc_list_flush (slave->dev, master->mc_list); + + /* unset promiscuity level from slave */ + if(master->flags & IFF_PROMISC) + dev_set_promiscuity(slave->dev, -1); + + /* unset allmulti level from slave */ + if(master->flags & IFF_ALLMULTI) + dev_set_allmulti(slave->dev, -1); + spin_unlock_bh(&master->xmit_lock); netdev_set_master(slave->dev, NULL); @@ -111,21 +128,81 @@ while ((slave = bond->next) != (slave_t*)bond) release_one_slave(master, slave); + dev_mc_list_destroy (&bond->mc_list); MOD_DEC_USE_COUNT; return 0; } +static void bond_mc_add(bonding_t *bond, void *addr, int alen) +{ + slave_t *slave; + + for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) + dev_mc_add(slave->dev, addr, alen, 0); +} + +static void bond_mc_delete(bonding_t *bond, void *addr, int alen) +{ + slave_t *slave; + + for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) + dev_mc_delete(slave->dev, addr, alen, 0); +} + +static void bond_set_promiscuity(bonding_t *bond, int inc) +{ + slave_t *slave; + + for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) + dev_set_promiscuity(slave->dev, inc); +} + +static void bond_set_allmulti(bonding_t *bond, int inc) +{ + slave_t *slave; + + for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) + dev_set_allmulti(slave->dev, inc); +} + static void bond_set_multicast_list(struct net_device *master) { bonding_t *bond = master->priv; - slave_t *slave; + struct dev_mc_list *dmi; + - for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) { - slave->dev->mc_list = master->mc_list; - slave->dev->mc_count = master->mc_count; - slave->dev->flags = master->flags; - slave->dev->set_multicast_list(slave->dev); + /* set promiscuity flag to slaves */ + if( (master->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC) ) + bond_set_promiscuity(bond, 1); + + if( !(master->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC) ) + bond_set_promiscuity(bond, -1); + + /* set allmulti flag to slaves */ + if( (master->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI) ) + bond_set_allmulti(bond, 1); + + if( !(master->flags & IFF_ALLMULTI) && (bond->flags & IFF_ALLMULTI) ) + bond_set_allmulti(bond, -1); + + bond->flags = master->flags; + + /* looking for addresses to add to slaves' mc list */ + for(dmi=master->mc_list; dmi!=NULL; dmi=dmi->next) { + if(dev_mc_list_find_dmi(dmi, bond->mc_list) == NULL) + bond_mc_add(bond, dmi->dmi_addr, dmi->dmi_addrlen); + } + + /* looking for addresses to delete from slaves' list */ + for(dmi=bond->mc_list; dmi!=NULL; dmi=dmi->next) { + if(dev_mc_list_find_dmi(dmi, master->mc_list) == NULL) + bond_mc_delete(bond, dmi->dmi_addr, dmi->dmi_addrlen); + } + + /* save master's multicast list */ + dev_mc_list_destroy (&bond->mc_list); + dev_mc_list_copy (master->mc_list, &bond->mc_list, GFP_KERNEL); } static int bond_enslave(struct net_device *master, struct net_device *dev) @@ -133,7 +210,8 @@ int err; bonding_t *bond = master->priv; slave_t *slave; - + struct dev_mc_list *dmi; + if (dev->type != master->type) return -ENODEV; @@ -151,6 +229,18 @@ slave->dev = dev; spin_lock_bh(&master->xmit_lock); + + /* set promiscuity level to new slave */ + if(master->flags & IFF_PROMISC) + dev_set_promiscuity(dev, 1); + + /* set allmulti level to new slave */ + if(master->flags & IFF_ALLMULTI) + dev_set_allmulti(dev, 1); + + /* upload master's mc_list to new slave */ + for(dmi=master->mc_list; dmi!=NULL; dmi=dmi->next) + dev_mc_add (dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); dev_hold(dev); diff -urN -X exclude 2.4.0-test7-pre7/include/linux/netdevice.h 2.4.0-test7-pre7-bond/include/linux/netdevice.h --- 2.4.0-test7-pre7/include/linux/netdevice.h Thu Aug 24 13:50:48 2000 +++ 2.4.0-test7-pre7-bond/include/linux/netdevice.h Thu Aug 24 14:22:06 2000 @@ -619,6 +619,10 @@ extern int dev_mc_delete(struct net_device *dev, void *addr, int alen, int all); extern int dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly); extern void dev_mc_discard(struct net_device *dev); +extern struct dev_mc_list* dev_mc_list_find_dmi (struct dev_mc_list *dmi, struct dev_mc_list *mc_list); +extern int dev_mc_list_copy (struct dev_mc_list *from, struct dev_mc_list **to, int gpf_flag); +extern void dev_mc_list_destroy(struct dev_mc_list **mc_list); +extern void dev_mc_list_flush(struct net_device *dev, struct dev_mc_list *mc_list); extern void dev_set_promiscuity(struct net_device *dev, int inc); extern void dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); diff -urN -X exclude 2.4.0-test7-pre7/net/core/dev_mcast.c 2.4.0-test7-pre7-bond/net/core/dev_mcast.c --- 2.4.0-test7-pre7/net/core/dev_mcast.c Thu Aug 24 13:38:07 2000 +++ 2.4.0-test7-pre7-bond/net/core/dev_mcast.c Thu Aug 24 16:20:45 2000 @@ -14,6 +14,8 @@ * Alan Cox : IFF_ALLMULTI support. * Alan Cox : New format set_multicast_list() calls. * Gleb Natapov : Remove dev_mc_lock. + * Gleb Natapov : various mc_list manipulation functions + * added * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -95,6 +97,7 @@ __dev_mc_upload(dev); spin_unlock_bh(&dev->xmit_lock); } + /* * Delete a device level multicast */ @@ -214,6 +217,80 @@ dev->mc_count = 0; spin_unlock_bh(&dev->xmit_lock); +} + +/* + * compare two mc addresses + */ +static inline int dmi_same(struct dev_mc_list *dmi1, struct dev_mc_list *dmi2) +{ + return memcmp(dmi1->dmi_addr,dmi2->dmi_addr,dmi1->dmi_addrlen) == 0 + && dmi1->dmi_addrlen == dmi2->dmi_addrlen; +} + +/* + * returns dmi entry if found, NULL otherwise + */ +struct dev_mc_list* dev_mc_list_find_dmi (struct dev_mc_list *dmi, + struct dev_mc_list *mc_list) +{ + struct dev_mc_list *idmi; + + for(idmi=mc_list; idmi!=NULL; idmi=idmi->next) + if(dmi_same(dmi, idmi)) + return idmi; + + return NULL; +} + +/* + * copy mc_list + */ +int dev_mc_list_copy (struct dev_mc_list *from, struct dev_mc_list **to, + int gpf_flag) +{ + struct dev_mc_list *dmi, *new_dmi; + + for(dmi=from; dmi!=NULL; dmi=dmi->next) { + new_dmi = kmalloc(sizeof(struct dev_mc_list), gpf_flag); + if(new_dmi == NULL) + return -ENOMEM; + new_dmi->next = *to; + *to = new_dmi; + + new_dmi->dmi_addrlen = dmi->dmi_addrlen; + memcpy(new_dmi->dmi_addr, dmi->dmi_addr, dmi->dmi_addrlen); + new_dmi->dmi_users = dmi->dmi_users; + new_dmi->dmi_gusers = dmi->dmi_gusers; + } + + return 0; +} + +/* + * remove all elements of mc_list + */ +void dev_mc_list_destroy(struct dev_mc_list **mc_list) +{ + struct dev_mc_list *dmi = *mc_list; + + while(dmi) { + *mc_list = dmi->next; + kfree(dmi); + dmi = *mc_list; + } +} + +/* + * flush all members of mc_list from device dev + */ +void dev_mc_list_flush(struct net_device *dev, struct dev_mc_list *mc_list) +{ + struct dev_mc_list *dmi; + int i; + + for(dmi = mc_list; dmi!=NULL; dmi=dmi->next) + dev_mc_delete(dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); } #ifdef CONFIG_PROC_FS diff -urN -X exclude 2.4.0-test7-pre7/net/netsyms.c 2.4.0-test7-pre7-bond/net/netsyms.c --- 2.4.0-test7-pre7/net/netsyms.c Thu Aug 24 13:37:22 2000 +++ 2.4.0-test7-pre7-bond/net/netsyms.c Thu Aug 24 15:49:05 2000 @@ -501,6 +501,10 @@ EXPORT_SYMBOL(dev_mc_add); EXPORT_SYMBOL(dev_mc_delete); EXPORT_SYMBOL(dev_mc_upload); +EXPORT_SYMBOL(dev_mc_list_find_dmi); +EXPORT_SYMBOL(dev_mc_list_copy); +EXPORT_SYMBOL(dev_mc_list_destroy); +EXPORT_SYMBOL(dev_mc_list_flush); EXPORT_SYMBOL(n_tty_ioctl); EXPORT_SYMBOL(tty_register_ldisc); EXPORT_SYMBOL(__kill_fasync);

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Thu Aug 31 2000 - 21:00:13 EST