summaryrefslogtreecommitdiffstats
path: root/net/core/dev_mcast.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
commit59223edaa18759982db0a8aced0e77457d10c68e (patch)
tree89354903b01fa0a447bffeefe00df3044495db2e /net/core/dev_mcast.c
parentdb7d4daea91e105e3859cf461d7e53b9b77454b2 (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.c51
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)