diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
commit | 59223edaa18759982db0a8aced0e77457d10c68e (patch) | |
tree | 89354903b01fa0a447bffeefe00df3044495db2e /net/core/dev_mcast.c | |
parent | db7d4daea91e105e3859cf461d7e53b9b77454b2 (diff) |
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'net/core/dev_mcast.c')
-rw-r--r-- | net/core/dev_mcast.c | 51 |
1 files changed, 34 insertions, 17 deletions
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index bce3f4a4a..f7fcb1f87 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -58,7 +58,11 @@ * * Device mc lists are changed by bh at least if IPv6 is enabled, * so that it must be bh protected. + * + * We protect all mc lists with global rw lock + * and block accesses to device mc filters with dev->xmit_lock. */ +static rwlock_t dev_mc_lock = RW_LOCK_UNLOCKED; /* * Update the multicast list into the physical NIC controller. @@ -69,7 +73,7 @@ void dev_mc_upload(struct device *dev) /* Don't do anything till we up the interface [dev_open will call this function so the list will stay sane] */ - + if(!(dev->flags&IFF_UP)) return; @@ -80,11 +84,15 @@ void dev_mc_upload(struct device *dev) if(dev->set_multicast_list==NULL) return; - start_bh_atomic(); + read_lock_bh(&dev_mc_lock); + spin_lock(&dev->xmit_lock); + dev->xmit_lock_owner = smp_processor_id(); dev->set_multicast_list(dev); - end_bh_atomic(); + dev->xmit_lock_owner = -1; + spin_unlock(&dev->xmit_lock); + read_unlock_bh(&dev_mc_lock); } - + /* * Delete a device level multicast */ @@ -94,7 +102,7 @@ int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl) int err = 0; struct dev_mc_list *dmi, **dmip; - start_bh_atomic(); + write_lock_bh(&dev_mc_lock); for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) { /* * Find the entry we want to delete. The device could @@ -120,14 +128,15 @@ int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl) * We have altered the list, so the card * loaded filter is now wrong. Fix it */ - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); + dev_mc_upload(dev); return 0; } } err = -ENOENT; done: - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); return err; } @@ -140,9 +149,12 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl) int err = 0; struct dev_mc_list *dmi, *dmi1; + /* RED-PEN: does gfp_any() work now? It requires + true local_bh_disable rather than global. + */ dmi1 = (struct dev_mc_list *)kmalloc(sizeof(*dmi), gfp_any()); - start_bh_atomic(); + write_lock_bh(&dev_mc_lock); for(dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next) { if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) { if (glbl) { @@ -156,8 +168,10 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl) } } - if ((dmi=dmi1)==NULL) + if ((dmi=dmi1)==NULL) { + write_unlock_bh(&dev_mc_lock); return -ENOMEM; + } memcpy(dmi->dmi_addr, addr, alen); dmi->dmi_addrlen=alen; dmi->next=dev->mc_list; @@ -165,12 +179,12 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl) dmi->dmi_gusers=glbl ? 1 : 0; dev->mc_list=dmi; dev->mc_count++; - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); dev_mc_upload(dev); return 0; done: - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); if (dmi1) kfree(dmi1); return err; @@ -182,7 +196,7 @@ done: void dev_mc_discard(struct device *dev) { - start_bh_atomic(); + write_lock_bh(&dev_mc_lock); while (dev->mc_list!=NULL) { struct dev_mc_list *tmp=dev->mc_list; dev->mc_list=tmp->next; @@ -191,7 +205,7 @@ void dev_mc_discard(struct device *dev) kfree_s(tmp,sizeof(*tmp)); } dev->mc_count=0; - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); } #ifdef CONFIG_PROC_FS @@ -203,9 +217,9 @@ static int dev_mc_read_proc(char *buffer, char **start, off_t offset, int len=0; struct device *dev; - start_bh_atomic(); - + read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) { + read_lock_bh(&dev_mc_lock); for (m = dev->mc_list; m; m = m->next) { int i; @@ -222,14 +236,17 @@ static int dev_mc_read_proc(char *buffer, char **start, off_t offset, len=0; begin=pos; } - if (pos > offset+length) + if (pos > offset+length) { + read_unlock_bh(&dev_mc_lock); goto done; + } } + read_unlock_bh(&dev_mc_lock); } *eof = 1; done: - end_bh_atomic(); + read_unlock(&dev_base_lock); *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) |