diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 19 |
1 files changed, 17 insertions, 2 deletions
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 433602aed468..c24d2fa3e3b6 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c | |||
@@ -267,6 +267,7 @@ static int hiddev_open(struct inode *inode, struct file *file) | |||
267 | struct hiddev_list *list; | 267 | struct hiddev_list *list; |
268 | int res, i; | 268 | int res, i; |
269 | 269 | ||
270 | /* See comment in hiddev_connect() for BKL explanation */ | ||
270 | lock_kernel(); | 271 | lock_kernel(); |
271 | i = iminor(inode) - HIDDEV_MINOR_BASE; | 272 | i = iminor(inode) - HIDDEV_MINOR_BASE; |
272 | 273 | ||
@@ -894,8 +895,22 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) | |||
894 | hiddev->hid = hid; | 895 | hiddev->hid = hid; |
895 | hiddev->exist = 1; | 896 | hiddev->exist = 1; |
896 | 897 | ||
897 | /* when lock_kernel() usage is fixed in usb_open(), | 898 | /* |
898 | * we could also fix it here */ | 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 | */ | ||
899 | lock_kernel(); | 914 | lock_kernel(); |
900 | retval = usb_register_dev(usbhid->intf, &hiddev_class); | 915 | retval = usb_register_dev(usbhid->intf, &hiddev_class); |
901 | if (retval) { | 916 | if (retval) { |