Skip to content

Commit 1deb9d3

Browse files
author
Jiri Kosina
committed
HID: debug: fix RCU preemption issue
Commit 2353f2b ("HID: protect hid_debug_list") introduced mutex locking around debug_list access to prevent SMP races when debugfs nodes are being operated upon by multiple userspace processess. mutex is not a proper synchronization primitive though, as the hid-debug callbacks are being called from atomic contexts. We also have to be careful about disabling IRQs when taking the lock to prevent deadlock against IRQ handlers. Benjamin reports this has also been reported in RH bugzilla as bug #958935. =============================== [ INFO: suspicious RCU usage. ] 3.9.0+ #94 Not tainted ------------------------------- include/linux/rcupdate.h:476 Illegal context switch in RCU read-side critical section! other info that might help us debug this: rcu_scheduler_active = 1, debug_locks = 0 4 locks held by Xorg/5502: #0: (&evdev->mutex){+.+...}, at: [<ffffffff81512c3d>] evdev_write+0x6d/0x160 #1: (&(&dev->event_lock)->rlock#2){-.-...}, at: [<ffffffff8150dd9b>] input_inject_event+0x5b/0x230 #2: (rcu_read_lock){.+.+..}, at: [<ffffffff8150dd82>] input_inject_event+0x42/0x230 #3: (&(&usbhid->lock)->rlock){-.....}, at: [<ffffffff81565289>] usb_hidinput_input_event+0x89/0x120 stack backtrace: CPU: 0 PID: 5502 Comm: Xorg Not tainted 3.9.0+ #94 Hardware name: Dell Inc. OptiPlex 390/0M5DCD, BIOS A09 07/24/2012 0000000000000001 ffff8800689c7c38 ffffffff816f249f ffff8800689c7c68 ffffffff810acb1d 0000000000000000 ffffffff81a03ac7 000000000000019d 0000000000000000 ffff8800689c7c90 ffffffff8107cda7 0000000000000000 Call Trace: [<ffffffff816f249f>] dump_stack+0x19/0x1b [<ffffffff810acb1d>] lockdep_rcu_suspicious+0xfd/0x130 [<ffffffff8107cda7>] __might_sleep+0xc7/0x230 [<ffffffff816f7770>] mutex_lock_nested+0x40/0x3a0 [<ffffffff81312ac4>] ? vsnprintf+0x354/0x640 [<ffffffff81553cc4>] hid_debug_event+0x34/0x100 [<ffffffff81554197>] hid_dump_input+0x67/0xa0 [<ffffffff81556430>] hid_set_field+0x50/0x120 [<ffffffff8156529a>] usb_hidinput_input_event+0x9a/0x120 [<ffffffff8150d89e>] input_handle_event+0x8e/0x530 [<ffffffff8150df10>] input_inject_event+0x1d0/0x230 [<ffffffff8150dd82>] ? input_inject_event+0x42/0x230 [<ffffffff81512cae>] evdev_write+0xde/0x160 [<ffffffff81185038>] vfs_write+0xc8/0x1f0 [<ffffffff81185535>] SyS_write+0x55/0xa0 [<ffffffff81704482>] system_call_fastpath+0x16/0x1b BUG: sleeping function called from invalid context at kernel/mutex.c:413 in_atomic(): 1, irqs_disabled(): 1, pid: 5502, name: Xorg INFO: lockdep is turned off. irq event stamp: 1098574 hardirqs last enabled at (1098573): [<ffffffff816fb53f>] _raw_spin_unlock_irqrestore+0x3f/0x70 hardirqs last disabled at (1098574): [<ffffffff816faaf5>] _raw_spin_lock_irqsave+0x25/0xa0 softirqs last enabled at (1098306): [<ffffffff8104971f>] __do_softirq+0x18f/0x3c0 softirqs last disabled at (1097867): [<ffffffff81049ad5>] irq_exit+0xa5/0xb0 CPU: 0 PID: 5502 Comm: Xorg Not tainted 3.9.0+ #94 Hardware name: Dell Inc. OptiPlex 390/0M5DCD, BIOS A09 07/24/2012 ffffffff81a03ac7 ffff8800689c7c68 ffffffff816f249f ffff8800689c7c90 ffffffff8107ce60 0000000000000000 ffff8800689c7fd8 ffff88006a62c800 ffff8800689c7d10 ffffffff816f7770 ffff8800689c7d00 ffffffff81312ac4 Call Trace: [<ffffffff816f249f>] dump_stack+0x19/0x1b [<ffffffff8107ce60>] __might_sleep+0x180/0x230 [<ffffffff816f7770>] mutex_lock_nested+0x40/0x3a0 [<ffffffff81312ac4>] ? vsnprintf+0x354/0x640 [<ffffffff81553cc4>] hid_debug_event+0x34/0x100 [<ffffffff81554197>] hid_dump_input+0x67/0xa0 [<ffffffff81556430>] hid_set_field+0x50/0x120 [<ffffffff8156529a>] usb_hidinput_input_event+0x9a/0x120 [<ffffffff8150d89e>] input_handle_event+0x8e/0x530 [<ffffffff8150df10>] input_inject_event+0x1d0/0x230 [<ffffffff8150dd82>] ? input_inject_event+0x42/0x230 [<ffffffff81512cae>] evdev_write+0xde/0x160 [<ffffffff81185038>] vfs_write+0xc8/0x1f0 [<ffffffff81185535>] SyS_write+0x55/0xa0 [<ffffffff81704482>] system_call_fastpath+0x16/0x1b Reported-by: majianpeng <majianpeng@gmail.com> Reported-by: Benjamin Tissoires <benjamin.tissoires@gmail.com> Reviewed-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
1 parent b52b506 commit 1deb9d3

3 files changed

Lines changed: 11 additions & 8 deletions

File tree

drivers/hid/hid-core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2342,7 +2342,7 @@ struct hid_device *hid_allocate_device(void)
23422342

23432343
init_waitqueue_head(&hdev->debug_wait);
23442344
INIT_LIST_HEAD(&hdev->debug_list);
2345-
mutex_init(&hdev->debug_list_lock);
2345+
spin_lock_init(&hdev->debug_list_lock);
23462346
sema_init(&hdev->driver_lock, 1);
23472347
sema_init(&hdev->driver_input_lock, 1);
23482348

drivers/hid/hid-debug.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -579,15 +579,16 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
579579
{
580580
int i;
581581
struct hid_debug_list *list;
582+
unsigned long flags;
582583

583-
mutex_lock(&hdev->debug_list_lock);
584+
spin_lock_irqsave(&hdev->debug_list_lock, flags);
584585
list_for_each_entry(list, &hdev->debug_list, node) {
585586
for (i = 0; i < strlen(buf); i++)
586587
list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
587588
buf[i];
588589
list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
589590
}
590-
mutex_unlock(&hdev->debug_list_lock);
591+
spin_unlock_irqrestore(&hdev->debug_list_lock, flags);
591592

592593
wake_up_interruptible(&hdev->debug_wait);
593594
}
@@ -977,6 +978,7 @@ static int hid_debug_events_open(struct inode *inode, struct file *file)
977978
{
978979
int err = 0;
979980
struct hid_debug_list *list;
981+
unsigned long flags;
980982

981983
if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) {
982984
err = -ENOMEM;
@@ -992,9 +994,9 @@ static int hid_debug_events_open(struct inode *inode, struct file *file)
992994
file->private_data = list;
993995
mutex_init(&list->read_mutex);
994996

995-
mutex_lock(&list->hdev->debug_list_lock);
997+
spin_lock_irqsave(&list->hdev->debug_list_lock, flags);
996998
list_add_tail(&list->node, &list->hdev->debug_list);
997-
mutex_unlock(&list->hdev->debug_list_lock);
999+
spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags);
9981000

9991001
out:
10001002
return err;
@@ -1088,10 +1090,11 @@ static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait)
10881090
static int hid_debug_events_release(struct inode *inode, struct file *file)
10891091
{
10901092
struct hid_debug_list *list = file->private_data;
1093+
unsigned long flags;
10911094

1092-
mutex_lock(&list->hdev->debug_list_lock);
1095+
spin_lock_irqsave(&list->hdev->debug_list_lock, flags);
10931096
list_del(&list->node);
1094-
mutex_unlock(&list->hdev->debug_list_lock);
1097+
spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags);
10951098
kfree(list->hid_debug_buf);
10961099
kfree(list);
10971100

include/linux/hid.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ struct hid_device { /* device report descriptor */
515515
struct dentry *debug_rdesc;
516516
struct dentry *debug_events;
517517
struct list_head debug_list;
518-
struct mutex debug_list_lock;
518+
spinlock_t debug_list_lock;
519519
wait_queue_head_t debug_wait;
520520
};
521521

0 commit comments

Comments
 (0)