aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2010-07-11 09:34:05 -0400
committerJiri Kosina <jkosina@suse.cz>2010-07-13 17:56:30 -0400
commitbd25f4dd6972755579d0ea50d1a5ace2e9b00d1a (patch)
tree0aec56e60352d198514f5af933bd0399d413ec35 /drivers/hid
parent1c5474a65bf15a4cb162dfff86d6d0b5a08a740c (diff)
HID: hiddev: use usb_find_interface, get rid of BKL
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. This doesn't introduce race condition -- the intf pointer could be invalidated only in hiddev_disconnect() through usb_deregister_dev(), but that will block on minor_rwsem and not actually remove the device until usb_open(). Signed-off-by: Arnd Bergmann <arnd@arndb.de> Cc: Jiri Kosina <jkosina@suse.cz> Cc: "Greg Kroah-Hartman" <gregkh@suse.de> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/usbhid/hiddev.c54
1 files changed, 12 insertions, 42 deletions
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index c24d2fa3e3b6..254a003af048 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -67,7 +67,7 @@ struct hiddev_list {
67 struct mutex thread_lock; 67 struct mutex thread_lock;
68}; 68};
69 69
70static struct hiddev *hiddev_table[HIDDEV_MINORS]; 70static struct usb_driver hiddev_driver;
71 71
72/* 72/*
73 * Find a report, given the report's type and ID. The ID can be specified 73 * 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)
265static int hiddev_open(struct inode *inode, struct file *file) 265static int hiddev_open(struct inode *inode, struct file *file)
266{ 266{
267 struct hiddev_list *list; 267 struct hiddev_list *list;
268 int res, i; 268 struct usb_interface *intf;
269 269 struct hiddev *hiddev;
270 /* See comment in hiddev_connect() for BKL explanation */ 270 int res;
271 lock_kernel();
272 i = iminor(inode) - HIDDEV_MINOR_BASE;
273 271
274 if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i]) 272 intf = usb_find_interface(&hiddev_driver, iminor(inode));
273 if (!intf)
275 return -ENODEV; 274 return -ENODEV;
275 hiddev = usb_get_intfdata(intf);
276 276
277 if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) 277 if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
278 return -ENOMEM; 278 return -ENOMEM;
279 mutex_init(&list->thread_lock); 279 mutex_init(&list->thread_lock);
280 280 list->hiddev = hiddev;
281 list->hiddev = hiddev_table[i];
282
283
284 file->private_data = list; 281 file->private_data = list;
285 282
286 /* 283 /*
@@ -289,7 +286,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
289 */ 286 */
290 if (list->hiddev->exist) { 287 if (list->hiddev->exist) {
291 if (!list->hiddev->open++) { 288 if (!list->hiddev->open++) {
292 res = usbhid_open(hiddev_table[i]->hid); 289 res = usbhid_open(hiddev->hid);
293 if (res < 0) { 290 if (res < 0) {
294 res = -EIO; 291 res = -EIO;
295 goto bail; 292 goto bail;
@@ -301,12 +298,12 @@ static int hiddev_open(struct inode *inode, struct file *file)
301 } 298 }
302 299
303 spin_lock_irq(&list->hiddev->list_lock); 300 spin_lock_irq(&list->hiddev->list_lock);
304 list_add_tail(&list->node, &hiddev_table[i]->list); 301 list_add_tail(&list->node, &hiddev->list);
305 spin_unlock_irq(&list->hiddev->list_lock); 302 spin_unlock_irq(&list->hiddev->list_lock);
306 303
307 if (!list->hiddev->open++) 304 if (!list->hiddev->open++)
308 if (list->hiddev->exist) { 305 if (list->hiddev->exist) {
309 struct hid_device *hid = hiddev_table[i]->hid; 306 struct hid_device *hid = hiddev->hid;
310 res = usbhid_get_power(hid); 307 res = usbhid_get_power(hid);
311 if (res < 0) { 308 if (res < 0) {
312 res = -EIO; 309 res = -EIO;
@@ -314,13 +311,10 @@ static int hiddev_open(struct inode *inode, struct file *file)
314 } 311 }
315 usbhid_open(hid); 312 usbhid_open(hid);
316 } 313 }
317
318 unlock_kernel();
319 return 0; 314 return 0;
320bail: 315bail:
321 file->private_data = NULL; 316 file->private_data = NULL;
322 kfree(list); 317 kfree(list);
323 unlock_kernel();
324 return res; 318 return res;
325} 319}
326 320
@@ -894,37 +888,14 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
894 hid->hiddev = hiddev; 888 hid->hiddev = hiddev;
895 hiddev->hid = hid; 889 hiddev->hid = hid;
896 hiddev->exist = 1; 890 hiddev->exist = 1;
897 891 usb_set_intfdata(usbhid->intf, usbhid);
898 /*
899 * BKL here is used to avoid race after usb_register_dev().
900 * Once the device node has been created, open() could happen on it.
901 * The code below will then fail, as hiddev_table hasn't been
902 * updated.
903 *
904 * The obvious fix -- introducing mutex to guard hiddev_table[]
905 * doesn't work, as usb_open() and usb_register_dev() both take
906 * minor_rwsem, thus we'll have ABBA deadlock.
907 *
908 * Before BKL pushdown, usb_open() had been acquiring it in right
909 * order, so _open() was safe to use it to protect from this race.
910 * Now the order is different, but AB-BA deadlock still doesn't occur
911 * as BKL is dropped on schedule() (i.e. while sleeping on
912 * minor_rwsem). Fugly.
913 */
914 lock_kernel();
915 retval = usb_register_dev(usbhid->intf, &hiddev_class); 892 retval = usb_register_dev(usbhid->intf, &hiddev_class);
916 if (retval) { 893 if (retval) {
917 err_hid("Not able to get a minor for this device."); 894 err_hid("Not able to get a minor for this device.");
918 hid->hiddev = NULL; 895 hid->hiddev = NULL;
919 unlock_kernel();
920 kfree(hiddev); 896 kfree(hiddev);
921 return -1; 897 return -1;
922 } else {
923 hid->minor = usbhid->intf->minor;
924 hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
925 } 898 }
926 unlock_kernel();
927
928 return 0; 899 return 0;
929} 900}
930 901
@@ -942,7 +913,6 @@ void hiddev_disconnect(struct hid_device *hid)
942 hiddev->exist = 0; 913 hiddev->exist = 0;
943 mutex_unlock(&hiddev->existancelock); 914 mutex_unlock(&hiddev->existancelock);
944 915
945 hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
946 usb_deregister_dev(usbhid->intf, &hiddev_class); 916 usb_deregister_dev(usbhid->intf, &hiddev_class);
947 917
948 if (hiddev->open) { 918 if (hiddev->open) {