[PATCH] hiddev: use usb_find_interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This removes the private hiddev_table in the usbhid
driver and changes it to use usb_find_interface
instead.

The advantage is that we can avoid the race between
usb_register_dev and usb_open and no longer need the
big kernel lock.

Signed-off-by: Arnd Bergmann <arnd@xxxxxxxx>
Cc: Jiri Kosina <jkosina@xxxxxxx>
Cc: "Greg Kroah-Hartman" <gregkh@xxxxxxx>
---
Jiri, this fixes the race you outlined in the comment
and I believe the serialization for the remaining code
should be covered by usb_open taking minor_rwsem.

 drivers/hid/usbhid/hiddev.c |   54 +++++++++---------------------------------
 1 files changed, 12 insertions(+), 42 deletions(-)

diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 39157f3..eee8b13 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -67,7 +67,7 @@ struct hiddev_list {
 	struct mutex thread_lock;
 };
 
-static struct hiddev *hiddev_table[HIDDEV_MINORS];
+static struct usb_driver hiddev_driver;
 
 /*
  * Find a report, given the report's type and ID.  The ID can be specified
@@ -265,22 +265,19 @@ static int hiddev_release(struct inode * inode, struct file * file)
 static int hiddev_open(struct inode *inode, struct file *file)
 {
 	struct hiddev_list *list;
-	int res, i;
-
-	/* See comment in hiddev_connect() for BKL explanation */
-	lock_kernel();
-	i = iminor(inode) - HIDDEV_MINOR_BASE;
+	struct usb_interface *intf;
+	struct hiddev *hiddev;
+	int res;
 
-	if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i])
+	intf = usb_find_interface(&hiddev_driver, iminor(inode));
+	if (!intf)
 		return -ENODEV;
+	hiddev = usb_get_intfdata(intf);
 
 	if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
 		return -ENOMEM;
 	mutex_init(&list->thread_lock);
-
-	list->hiddev = hiddev_table[i];
-
-
+	list->hiddev = hiddev;
 	file->private_data = list;
 
 	/*
@@ -289,7 +286,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
 	 */
 	if (list->hiddev->exist) {
 		if (!list->hiddev->open++) {
-			res = usbhid_open(hiddev_table[i]->hid);
+			res = usbhid_open(hiddev->hid);
 			if (res < 0) {
 				res = -EIO;
 				goto bail;
@@ -301,12 +298,12 @@ static int hiddev_open(struct inode *inode, struct file *file)
 	}
 
 	spin_lock_irq(&list->hiddev->list_lock);
-	list_add_tail(&list->node, &hiddev_table[i]->list);
+	list_add_tail(&list->node, &hiddev->list);
 	spin_unlock_irq(&list->hiddev->list_lock);
 
 	if (!list->hiddev->open++)
 		if (list->hiddev->exist) {
-			struct hid_device *hid = hiddev_table[i]->hid;
+			struct hid_device *hid = hiddev->hid;
 			res = usbhid_get_power(hid);
 			if (res < 0) {
 				res = -EIO;
@@ -314,13 +311,10 @@ static int hiddev_open(struct inode *inode, struct file *file)
 			}
 			usbhid_open(hid);
 		}
-
-	unlock_kernel();
 	return 0;
 bail:
 	file->private_data = NULL;
 	kfree(list);
-	unlock_kernel();
 	return res;
 }
 
@@ -895,37 +889,14 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
 	hid->hiddev = hiddev;
 	hiddev->hid = hid;
 	hiddev->exist = 1;
-
-	/*
-	 * BKL here is used to avoid race after usb_register_dev().
-	 * Once the device node has been created, open() could happen on it.
-	 * The code below will then fail, as hiddev_table hasn't been
-	 * updated.
-	 *
-	 * The obvious fix -- introducing mutex to guard hiddev_table[]
-	 * doesn't work, as usb_open() and usb_register_dev() both take
-	 * minor_rwsem, thus we'll have ABBA deadlock.
-	 *
-	 * Before BKL pushdown, usb_open() had been acquiring it in right
-	 * order, so _open() was safe to use it to protect from this race.
-	 * Now the order is different, but AB-BA deadlock still doesn't occur
-	 * as BKL is dropped on schedule() (i.e. while sleeping on
-	 * minor_rwsem). Fugly.
-	 */
-	lock_kernel();
+	usb_set_intfdata(usbhid->intf, usbhid);
 	retval = usb_register_dev(usbhid->intf, &hiddev_class);
 	if (retval) {
 		err_hid("Not able to get a minor for this device.");
 		hid->hiddev = NULL;
-		unlock_kernel();
 		kfree(hiddev);
 		return -1;
-	} else {
-		hid->minor = usbhid->intf->minor;
-		hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
 	}
-	unlock_kernel();
-
 	return 0;
 }
 
@@ -943,7 +914,6 @@ void hiddev_disconnect(struct hid_device *hid)
 	hiddev->exist = 0;
 	mutex_unlock(&hiddev->existancelock);
 
-	hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
 	usb_deregister_dev(usbhid->intf, &hiddev_class);
 
 	if (hiddev->open) {
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux