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

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

diff -u --recursive --new-file v2.3.5/linux/net/core/dev_mcast.c linux/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 @@
 	/* 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 @@
 	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 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 @@
 			 *	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 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 @@
 		}
 	}
 
-	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 @@
 	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 @@
 
 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 @@
 		kfree_s(tmp,sizeof(*tmp));
 	}
 	dev->mc_count=0;
-	end_bh_atomic();
+	write_unlock_bh(&dev_mc_lock);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -203,8 +217,9 @@
 	int len=0;
 	struct device *dev;
 
-	read_lock_bh(&dev_base_lock);
+	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;
 
@@ -221,14 +236,17 @@
 				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:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)

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