<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.739   -&gt; 1.740  
#	drivers/usb/core/drivers.c	1.8     -&gt; 1.9    
#	drivers/usb/class/printer.c	1.28    -&gt; 1.29   
#	 include/linux/usb.h	1.40    -&gt; 1.41   
#	drivers/usb/core/devices.c	1.15    -&gt; 1.16   
#	drivers/usb/core/hub.c	1.29    -&gt; 1.30   
#	drivers/usb/core/devio.c	1.28    -&gt; 1.29   
#	drivers/usb/core/usb.c	1.66    -&gt; 1.67   
#	drivers/usb/media/pwc-if.c	1.22    -&gt; 1.23   
#	drivers/usb/core/file.c	1.1     -&gt; 1.2    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/07/18	oliver@neukum.name	1.740
# [PATCH] USB: lots of locking and other SMP race fixes
# 
# This is a merge of a bunch of SMP and locking fixes for the USB code
# that Oliver has sent me (greg k-h) over the past few weeks.
# --------------------------------------------
#
diff -Nru a/drivers/usb/class/printer.c b/drivers/usb/class/printer.c
--- a/drivers/usb/class/printer.c	Fri Jul 19 11:04:14 2002
+++ b/drivers/usb/class/printer.c	Fri Jul 19 11:04:14 2002
@@ -655,7 +655,10 @@
 							  (count - writecount) : USBLP_BUF_SIZE;
 
 		if (copy_from_user(usblp-&gt;writeurb-&gt;transfer_buffer, buffer + writecount,
-				usblp-&gt;writeurb-&gt;transfer_buffer_length)) return -EFAULT;
+				usblp-&gt;writeurb-&gt;transfer_buffer_length)) {
+			up(&amp;usblp-&gt;sem);
+			return writecount ? writecount : -EFAULT;
+		}
 
 		usblp-&gt;writeurb-&gt;dev = usblp-&gt;dev;
 		usblp-&gt;wcomplete = 0;
diff -Nru a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
--- a/drivers/usb/core/devices.c	Fri Jul 19 11:04:14 2002
+++ b/drivers/usb/core/devices.c	Fri Jul 19 11:04:14 2002
@@ -152,8 +152,8 @@
 
 void usbdevfs_conn_disc_event(void)
 {
-	wake_up(&amp;deviceconndiscwq);
 	conndiscevcnt++;
+	wake_up(&amp;deviceconndiscwq);
 }
 
 static const char *class_decode(const int class)
@@ -239,6 +239,7 @@
 
 	if (start &gt; end)
 		return start;
+	lock_kernel(); /* driver might be unloaded */
 	start += sprintf(start, format_iface,
 			 desc-&gt;bInterfaceNumber,
 			 desc-&gt;bAlternateSetting,
@@ -248,6 +249,7 @@
 			 desc-&gt;bInterfaceSubClass,
 			 desc-&gt;bInterfaceProtocol,
 			 iface-&gt;driver ? iface-&gt;driver-&gt;name : "(none)");
+	unlock_kernel();
 	return start;
 }
 
@@ -597,6 +599,13 @@
 			unlock_kernel();
 			return POLLIN;
 		}
+		
+		/* we may have dropped BKL - need to check for having lost the race */
+		if (file-&gt;private_data) {
+			kfree(st);
+			goto lost_race;
+		}
+
 		/*
 		 * need to prevent the module from being unloaded, since
 		 * proc_unregister does not call the release method and
@@ -606,6 +615,7 @@
 		file-&gt;private_data = st;
 		mask = POLLIN;
 	}
+lost_race:
 	if (file-&gt;f_mode &amp; FMODE_READ)
                 poll_wait(file, &amp;deviceconndiscwq, wait);
 	if (st-&gt;lastev != conndiscevcnt)
diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
--- a/drivers/usb/core/devio.c	Fri Jul 19 11:04:14 2002
+++ b/drivers/usb/core/devio.c	Fri Jul 19 11:04:14 2002
@@ -361,14 +361,14 @@
 	if (intf &gt;= 8*sizeof(ps-&gt;ifclaimed))
 		return -EINVAL;
 	err = -EINVAL;
-	lock_kernel();
 	dev = ps-&gt;dev;
+	down(&amp;dev-&gt;serialize);
 	if (dev &amp;&amp; test_and_clear_bit(intf, &amp;ps-&gt;ifclaimed)) {
 		iface = &amp;dev-&gt;actconfig-&gt;interface[intf];
 		usb_driver_release_interface(&amp;usbdevfs_driver, iface);
 		err = 0;
 	}
-	unlock_kernel();
+	up(&amp;dev-&gt;serialize);
 	return err;
 }
 
@@ -722,14 +722,11 @@
 		if (test_bit(i, &amp;ps-&gt;ifclaimed))
 			continue;
 
-		if (intf-&gt;driver) {
-			const struct usb_device_id *id;
-			down(&amp;intf-&gt;driver-&gt;serialize);
-			intf-&gt;driver-&gt;disconnect(ps-&gt;dev, intf-&gt;private_data);
-			id = usb_match_id(ps-&gt;dev,intf,intf-&gt;driver-&gt;id_table);
-			intf-&gt;driver-&gt;probe(ps-&gt;dev, i, id);
-			up(&amp;intf-&gt;driver-&gt;serialize);
+		lock_kernel();
+		if (intf-&gt;driver &amp;&amp; ps-&gt;dev) {
+			usb_bind_driver(intf-&gt;driver,ps-&gt;dev, i);
 		}
+		unlock_kernel();
 	}
 
 	return 0;
@@ -1092,16 +1089,17 @@
 
        /* disconnect kernel driver from interface, leaving it unbound.  */
        case USBDEVFS_DISCONNECT:
+       	/* this function is voodoo. without locking it is a maybe thing */
+		lock_kernel();
                driver = ifp-&gt;driver;
                if (driver) {
-                       down (&amp;driver-&gt;serialize);
                        dbg ("disconnect '%s' from dev %d interface %d",
                                driver-&gt;name, ps-&gt;dev-&gt;devnum, ctrl.ifno);
-                       driver-&gt;disconnect (ps-&gt;dev, ifp-&gt;private_data);
+		       usb_unbind_driver(ps-&gt;dev, ifp);
                        usb_driver_release_interface (driver, ifp);
-                       up (&amp;driver-&gt;serialize);
                } else
 			retval = -EINVAL;
+		unlock_kernel();
                break;
 
        /* let kernel drivers try to (re)bind to the interface */
@@ -1111,18 +1109,28 @@
 
        /* talk directly to the interface's driver */
        default:
+		lock_kernel(); /* against module unload */
                driver = ifp-&gt;driver;
-               if (driver == 0 || driver-&gt;ioctl == 0)
-                       retval = -ENOSYS;
-		else {
-			if (ifp-&gt;driver-&gt;owner)
+               if (driver == 0 || driver-&gt;ioctl == 0) {
+			unlock_kernel();
+			retval = -ENOSYS;
+		} else {
+			if (ifp-&gt;driver-&gt;owner) {
 				__MOD_INC_USE_COUNT(ifp-&gt;driver-&gt;owner);
+				unlock_kernel();
+			}
 			/* ifno might usefully be passed ... */
                        retval = driver-&gt;ioctl (ps-&gt;dev, ctrl.ioctl_code, buf);
 			/* size = min_t(int, size, retval)? */
-			if (ifp-&gt;driver-&gt;owner)
+			if (ifp-&gt;driver-&gt;owner) {
 				__MOD_DEC_USE_COUNT(ifp-&gt;driver-&gt;owner);
+			} else {
+				unlock_kernel();
+			}
 		}
+		
+		if (retval == -ENOIOCTLCMD)
+			retval = -ENOTTY;
 	}
 
 	/* cleanup and return */
@@ -1139,7 +1147,7 @@
 static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct dev_state *ps = (struct dev_state *)file-&gt;private_data;
-	int ret = -ENOIOCTLCMD;
+	int ret = -ENOTTY;
 
 	if (!(file-&gt;f_mode &amp; FMODE_WRITE))
 		return -EPERM;
diff -Nru a/drivers/usb/core/drivers.c b/drivers/usb/core/drivers.c
--- a/drivers/usb/core/drivers.c	Fri Jul 19 11:04:14 2002
+++ b/drivers/usb/core/drivers.c	Fri Jul 19 11:04:14 2002
@@ -66,6 +66,7 @@
 	start = page;
 	end = page + (PAGE_SIZE - 100);
 	pos = *ppos;
+	lock_kernel(); /* else drivers might be unloaded */
 	for (; tmp != &amp;usb_driver_list; tmp = tmp-&gt;next) {
 		struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
 		int minor = driver-&gt;fops ? driver-&gt;minor : -1;
@@ -80,6 +81,7 @@
 			break;
 		}
 	}
+	unlock_kernel();
 	if (start == page)
 		start += sprintf(start, "(none)\n");
 	len = start - page;
diff -Nru a/drivers/usb/core/file.c b/drivers/usb/core/file.c
--- a/drivers/usb/core/file.c	Fri Jul 19 11:04:14 2002
+++ b/drivers/usb/core/file.c	Fri Jul 19 11:04:14 2002
@@ -44,10 +44,13 @@
 
 	spin_lock (&amp;minor_lock);
 	c = usb_minors[minor];
-	spin_unlock (&amp;minor_lock);
 
-	if (!c || !(new_fops = fops_get(c)))
+	if (!c || !(new_fops = fops_get(c))) {
+		spin_unlock(&amp;minor_lock);
 		return err;
+	}
+	spin_unlock(&amp;minor_lock);
+
 	old_fops = file-&gt;f_op;
 	file-&gt;f_op = new_fops;
 	/* Curiouser and curiouser... NULL -&gt;open() as "no device" ? */
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c	Fri Jul 19 11:04:14 2002
+++ b/drivers/usb/core/hub.c	Fri Jul 19 11:04:14 2002
@@ -1046,8 +1046,6 @@
 
 static int usb_hub_thread(void *__hub)
 {
-	lock_kernel();
-
 	/*
 	 * This thread doesn't need any user-level access,
 	 * so get rid of all our resources
@@ -1067,8 +1065,6 @@
 	} while (!signal_pending(current));
 
 	dbg("usb_hub_thread exiting");
-
-	unlock_kernel();
 	complete_and_exit(&amp;khubd_exited, 0);
 }
 
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Fri Jul 19 11:04:14 2002
+++ b/drivers/usb/core/usb.c	Fri Jul 19 11:04:14 2002
@@ -32,6 +32,7 @@
 #include &lt;linux/init.h&gt;
 #include &lt;linux/spinlock.h&gt;
 #include &lt;linux/errno.h&gt;
+#include &lt;linux/smp_lock.h&gt;
 
 #ifdef CONFIG_USB_DEBUG
 	#define DEBUG
@@ -117,6 +118,108 @@
 	up (&amp;usb_bus_list_lock);
 }
 
+/**
+ *	usb_unbind_driver - disconnects a driver from a device
+ *	@device: usb device to be disconnected
+ *	@intf: interface of the device to be disconnected
+ *	Context: BKL held
+ *
+ *	Handles module usage count correctly
+ */
+
+void usb_unbind_driver(struct usb_device *device, struct usb_interface *intf)
+{
+	struct usb_driver *driver;
+	void *priv;
+	int m;
+	
+
+	driver = intf-&gt;driver;
+	priv = intf-&gt;private_data;
+	
+	if (!driver)
+		return;
+
+	/* as soon as we increase the module use count we drop the BKL
+	   before that we must not sleep */
+	if (driver-&gt;owner) {
+		m = try_inc_mod_count(driver-&gt;owner);
+		if (m == 0) {
+			err("Dieing driver still bound to device.\n");
+			return;
+		}
+		unlock_kernel();
+	}
+	down(&amp;driver-&gt;serialize); 	/* if we sleep here on an umanaged driver
+					   the holder of the lock guards against
+					   module unload */
+
+	driver-&gt;disconnect(device, priv);
+
+	up(&amp;driver-&gt;serialize);
+	if (driver-&gt;owner) {
+		lock_kernel();
+		__MOD_DEC_USE_COUNT(driver-&gt;owner);
+	}
+}
+
+/**
+ *	usb_bind_driver - connect a driver to a device's interface
+ *	@driver: device driver to be bound to a devices interface
+ *	@dev: device to be bound
+ *	@ifnum: index number of the interface to be used
+ *
+ *	Does a save binding of a driver to a device's interface
+ *	Returns a pointer to the drivers private description of the binding
+ */
+
+void *usb_bind_driver(struct usb_driver *driver, struct usb_device *dev, unsigned int ifnum)
+{
+	int i,m;
+	void *private = NULL;
+	const struct usb_device_id *id;
+	struct usb_interface *interface;
+
+	if (driver-&gt;owner) {
+		m = try_inc_mod_count(driver-&gt;owner);
+		if (m == 0)
+			return NULL; /* this horse is dead - don't ride*/
+		unlock_kernel();
+	}
+
+	interface = &amp;dev-&gt;actconfig-&gt;interface[ifnum];
+
+	id = driver-&gt;id_table;
+	/* new style driver? */
+	if (id) {
+		for (i = 0; i &lt; interface-&gt;num_altsetting; i++) {
+		  	interface-&gt;act_altsetting = i;
+			id = usb_match_id(dev, interface, id);
+			if (id) {
+				down(&amp;driver-&gt;serialize);
+				private = driver-&gt;probe(dev,ifnum,id);
+				up(&amp;driver-&gt;serialize);
+				if (private != NULL)
+					break;
+			}
+		}
+
+		/* if driver not bound, leave defaults unchanged */
+		if (private == NULL)
+			interface-&gt;act_altsetting = 0;
+	} else { /* "old style" driver */
+		down(&amp;driver-&gt;serialize);
+		private = driver-&gt;probe(dev, ifnum, NULL);
+		up(&amp;driver-&gt;serialize);
+	}
+	if (driver-&gt;owner) {
+		lock_kernel();
+		__MOD_DEC_USE_COUNT(driver-&gt;owner);
+	}
+
+	return private;
+}
+
 /*
  * This function is part of a depth-first search down the device tree,
  * removing any instances of a device driver.
@@ -136,18 +239,12 @@
 
 	if (!dev-&gt;actconfig)
 		return;
-			
+
 	for (i = 0; i &lt; dev-&gt;actconfig-&gt;bNumInterfaces; i++) {
 		struct usb_interface *interface = &amp;dev-&gt;actconfig-&gt;interface[i];
-		
+
 		if (interface-&gt;driver == driver) {
-			if (driver-&gt;owner)
-				__MOD_INC_USE_COUNT(driver-&gt;owner);
-			down(&amp;driver-&gt;serialize);
-			driver-&gt;disconnect(dev, interface-&gt;private_data);
-			up(&amp;driver-&gt;serialize);
-			if (driver-&gt;owner)
-				__MOD_DEC_USE_COUNT(driver-&gt;owner);
+			usb_unbind_driver(dev, interface);
 			/* if driver-&gt;disconnect didn't release the interface */
 			if (interface-&gt;driver)
 				usb_driver_release_interface(driver, interface);
@@ -163,7 +260,7 @@
 /**
  * usb_deregister - unregister a USB driver
  * @driver: USB operations of the driver to unregister
- * Context: !in_interrupt ()
+ * Context: !in_interrupt (), must be called with BKL held
  *
  * Unlinks the specified driver from the internal USB driver list.
  * 
@@ -528,9 +625,7 @@
 	struct list_head *tmp;
 	struct usb_interface *interface;
 	void *private;
-	const struct usb_device_id *id;
 	struct usb_driver *driver;
-	int i;
 	
 	if ((!dev) || (ifnum &gt;= dev-&gt;actconfig-&gt;bNumInterfaces)) {
 		err("bad find_interface_driver params");
@@ -545,37 +640,12 @@
 		goto out_err;
 
 	private = NULL;
+	lock_kernel();
 	for (tmp = usb_driver_list.next; tmp != &amp;usb_driver_list;) {
 		driver = list_entry(tmp, struct usb_driver, driver_list);
 		tmp = tmp-&gt;next;
 
-		if (driver-&gt;owner)
-			__MOD_INC_USE_COUNT(driver-&gt;owner);
-		id = driver-&gt;id_table;
-		/* new style driver? */
-		if (id) {
-			for (i = 0; i &lt; interface-&gt;num_altsetting; i++) {
-			  	interface-&gt;act_altsetting = i;
-				id = usb_match_id(dev, interface, id);
-				if (id) {
-					down(&amp;driver-&gt;serialize);
-					private = driver-&gt;probe(dev,ifnum,id);
-					up(&amp;driver-&gt;serialize);
-					if (private != NULL)
-						break;
-				}
-			}
-
-			/* if driver not bound, leave defaults unchanged */
-			if (private == NULL)
-				interface-&gt;act_altsetting = 0;
-		} else { /* "old style" driver */
-			down(&amp;driver-&gt;serialize);
-			private = driver-&gt;probe(dev, ifnum, NULL);
-			up(&amp;driver-&gt;serialize);
-		}
-		if (driver-&gt;owner)
-			__MOD_DEC_USE_COUNT(driver-&gt;owner);
+		private = usb_bind_driver(driver, dev, ifnum);
 
 		/* probe() may have changed the config on us */
 		interface = dev-&gt;actconfig-&gt;interface + ifnum;
@@ -583,9 +653,11 @@
 		if (private) {
 			usb_driver_claim_interface(driver, interface, private);
 			up(&amp;dev-&gt;serialize);
+			unlock_kernel();
 			return 0;
 		}
 	}
+	unlock_kernel();
 
 out_err:
 	up(&amp;dev-&gt;serialize);
@@ -1121,27 +1193,22 @@
 
 	info("USB disconnect on device %d", dev-&gt;devnum);
 
+	lock_kernel();
 	if (dev-&gt;actconfig) {
 		for (i = 0; i &lt; dev-&gt;actconfig-&gt;bNumInterfaces; i++) {
 			struct usb_interface *interface = &amp;dev-&gt;actconfig-&gt;interface[i];
 			struct usb_driver *driver = interface-&gt;driver;
 			if (driver) {
-				if (driver-&gt;owner)
-					__MOD_INC_USE_COUNT(driver-&gt;owner);
-				down(&amp;driver-&gt;serialize);
-				driver-&gt;disconnect(dev, interface-&gt;private_data);
-				up(&amp;driver-&gt;serialize);
+				usb_unbind_driver(dev, interface);
 				/* if driver-&gt;disconnect didn't release the interface */
 				if (interface-&gt;driver)
 					usb_driver_release_interface(driver, interface);
-				/* we don't need the driver any longer */
-				if (driver-&gt;owner)
-					__MOD_DEC_USE_COUNT(driver-&gt;owner);
 			}
 			/* remove our device node for this interface */
 			put_device(&amp;interface-&gt;dev);
 		}
 	}
+	unlock_kernel();
 
 	/* Free up all the children.. */
 	for (i = 0; i &lt; USB_MAXCHILDREN; i++) {
@@ -1475,6 +1542,8 @@
 EXPORT_SYMBOL(usb_reset_device);
 EXPORT_SYMBOL(usb_connect);
 EXPORT_SYMBOL(usb_disconnect);
+EXPORT_SYMBOL(usb_bind_driver);
+EXPORT_SYMBOL(usb_unbind_driver);
 
 EXPORT_SYMBOL(__usb_get_extra_descriptor);
 
diff -Nru a/drivers/usb/media/pwc-if.c b/drivers/usb/media/pwc-if.c
--- a/drivers/usb/media/pwc-if.c	Fri Jul 19 11:04:14 2002
+++ b/drivers/usb/media/pwc-if.c	Fri Jul 19 11:04:14 2002
@@ -1756,40 +1756,40 @@
 	pdev = (struct pwc_device *)ptr;
 	if (pdev == NULL) {
 		Err("pwc_disconnect() Called without private pointer.\n");
-		return;
+		goto out_err;
 	}
 	if (pdev-&gt;udev == NULL) {
 		Err("pwc_disconnect() already called for %p\n", pdev);
-		return;
+		goto out_err;
 	}
 	if (pdev-&gt;udev != udev) {
 		Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
-		return;
+		goto out_err;
 	}
 #ifdef PWC_MAGIC	
 	if (pdev-&gt;magic != PWC_MAGIC) {
 		Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
-		return;
+		goto out_err;
 	}
-#endif	
-	
+#endif
+
 	pdev-&gt;unplugged = 1;
 	if (pdev-&gt;vdev != NULL) {
-		video_unregister_device(pdev-&gt;vdev); 
+		video_unregister_device(pdev-&gt;vdev);
 		if (pdev-&gt;vopen) {
 			Info("Disconnected while device/video is open!\n");
-			
+
 			/* Wake up any processes that might be waiting for
 			   a frame, let them return an error condition
 			 */
 			wake_up(&amp;pdev-&gt;frameq);
-			
+
 			/* Wait until we get a 'go' from _close(). This used
 			   to have a gigantic race condition, since we kfree()
-			   stuff here, but we have to wait until close() 
-			   is finished. 
+			   stuff here, but we have to wait until close()
+			   is finished.
 			 */
-			   
+
 			Trace(TRACE_PROBE, "Sleeping on remove_ok.\n");
 			add_wait_queue(&amp;pdev-&gt;remove_ok, &amp;wait);
 			set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1815,6 +1815,7 @@
 			device_hint[hint].pdev = NULL;
 
 	pdev-&gt;udev = NULL;
+out_err:
 	unlock_kernel();
 	kfree(pdev);
 }
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Fri Jul 19 11:04:14 2002
+++ b/include/linux/usb.h	Fri Jul 19 11:04:14 2002
@@ -431,6 +431,10 @@
 /* for when layers above USB add new non-USB drivers */
 extern void usb_scan_devices(void);
 
+/* for probe/disconnect with correct module usage counting */
+void *usb_bind_driver(struct usb_driver *driver, struct usb_device *dev, unsigned int ifnum);
+void usb_unbind_driver(struct usb_device *device, struct usb_interface *intf);
+
 /* mostly for devices emulating SCSI over USB */
 extern int usb_reset_device(struct usb_device *dev);
 
</pre></body></html>