patch-2.2.4 linux/net/core/dev_mcast.c

Next file: linux/net/core/filter.c
Previous file: linux/net/core/dev.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.3/linux/net/core/dev_mcast.c linux/net/core/dev_mcast.c
@@ -56,10 +56,9 @@
  *	protocols without doing damage to the protocols when it deletes the
  *	entries. It also helps IP as it tracks overlapping maps.
  *
- *	BUGGGG! IPv6 calls dev_mac_add/delete from BH, it means
- *	that all the functions in this file are racy. [NOT FIXED] --ANK
+ *	Device mc lists are changed by bh at least if IPv6 is enabled,
+ *	so that it must be bh protected.
  */
- 
 
 /*
  *	Update the multicast list into the physical NIC controller.
@@ -77,11 +76,13 @@
 	/*
 	 *	Devices with no set multicast don't get set 
 	 */
-	 
+
 	if(dev->set_multicast_list==NULL)
 		return;
-		
+
+	start_bh_atomic();
 	dev->set_multicast_list(dev);
+	end_bh_atomic();
 }
   
 /*
@@ -90,8 +91,10 @@
  
 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();
 	for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) {
 		/*
 		 *	Find the entry we want to delete. The device could
@@ -102,10 +105,10 @@
 				int old_glbl = dmi->dmi_gusers;
 				dmi->dmi_gusers = 0;
 				if (old_glbl == 0)
-					return -ENOENT;
+					break;
 			}
 			if(--dmi->dmi_users)
-				return 0;
+				goto done;
 
 			/*
 			 *	Last user. So delete the entry.
@@ -117,11 +120,15 @@
 			 *	We have altered the list, so the card
 			 *	loaded filter is now wrong. Fix it
 			 */
+			end_bh_atomic();
 			dev_mc_upload(dev);
 			return 0;
 		}
 	}
-	return -ENOENT;
+	err = -ENOENT;
+done:
+	end_bh_atomic();
+	return err;
 }
 
 /*
@@ -130,30 +137,27 @@
  
 int dev_mc_add(struct device *dev, void *addr, int alen, int glbl)
 {
-	struct dev_mc_list *dmi;
+	int err = 0;
+	struct dev_mc_list *dmi, *dmi1;
+
+	dmi1 = (struct dev_mc_list *)kmalloc(sizeof(*dmi), gfp_any());
 
+	start_bh_atomic();
 	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) {
 				int old_glbl = dmi->dmi_gusers;
 				dmi->dmi_gusers = 1;
 				if (old_glbl)
-					return 0;
+					goto done;
 			}
 			dmi->dmi_users++;
-			return 0;
+			goto done;
 		}
 	}
 
-	/* GFP_ATOMIC!! It is used by IPv6 from interrupt,
-	   when new address arrives.
-
-	   Particularly, it means that this part of code is weirdly
-	   racy, and needs numerous *_bh_atomic --ANK
-	 */
-	dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi), GFP_ATOMIC);
-	if (dmi==NULL)
-		return -ENOBUFS;
+	if ((dmi=dmi1)==NULL)
+		return -ENOMEM;
 	memcpy(dmi->dmi_addr, addr, alen);
 	dmi->dmi_addrlen=alen;
 	dmi->next=dev->mc_list;
@@ -161,8 +165,15 @@
 	dmi->dmi_gusers=glbl ? 1 : 0;
 	dev->mc_list=dmi;
 	dev->mc_count++;
+	end_bh_atomic();
 	dev_mc_upload(dev);
 	return 0;
+
+done:
+	end_bh_atomic();
+	if (dmi1)
+		kfree(dmi1);
+	return err;
 }
 
 /*
@@ -171,6 +182,7 @@
 
 void dev_mc_discard(struct device *dev)
 {
+	start_bh_atomic();
 	while (dev->mc_list!=NULL) {
 		struct dev_mc_list *tmp=dev->mc_list;
 		dev->mc_list=tmp->next;
@@ -179,6 +191,7 @@
 		kfree_s(tmp,sizeof(*tmp));
 	}
 	dev->mc_count=0;
+	end_bh_atomic();
 }
 
 #ifdef CONFIG_PROC_FS
@@ -189,7 +202,9 @@
 	struct dev_mc_list *m;
 	int len=0;
 	struct device *dev;
-	
+
+	start_bh_atomic();
+
 	for (dev = dev_base; dev; dev = dev->next) {
 		for (m = dev->mc_list; m; m = m->next) {
 			int i;
@@ -214,10 +229,13 @@
 	*eof = 1;
 
 done:
+	end_bh_atomic();
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)
 		len=length;
+	if(len<0)
+		len=0;
 	return len;
 }
 #endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)