The mutex lock at rc_register_device() was added by commit
08aeb7c9a42a
("[media] rc: add locking to fix register/show race").
It is meant to avoid race issues when trying to open a sysfs file while
the RC register didn't complete.
Adding a lock there causes troubles, as detected by the Kernel lock
debug instrumentation at the Kernel:
======================================================
[ INFO: possible circular locking dependency detected ]
4.5.0-rc3+ #46 Not tainted
-------------------------------------------------------
systemd-udevd/2681 is trying to acquire lock:
(s_active#171){++++.+}, at: [<
ffffffff8171a115>] kernfs_remove_by_name_ns+0x45/0xa0
but task is already holding lock:
(&dev->lock){+.+.+.}, at: [<
ffffffffa0724def>] rc_register_device+0xb2f/0x1450 [rc_core]
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (&dev->lock){+.+.+.}:
[<
ffffffff8124817d>] lock_acquire+0x13d/0x320
[<
ffffffff822de966>] mutex_lock_nested+0xb6/0x860
[<
ffffffffa0721f2b>] show_protocols+0x3b/0x3f0 [rc_core]
[<
ffffffff81cdaba5>] dev_attr_show+0x45/0xc0
[<
ffffffff8171f1b3>] sysfs_kf_seq_show+0x203/0x3c0
[<
ffffffff8171a6a1>] kernfs_seq_show+0x121/0x1b0
[<
ffffffff81617c71>] seq_read+0x2f1/0x1160
[<
ffffffff8171c911>] kernfs_fop_read+0x321/0x460
[<
ffffffff815abc20>] __vfs_read+0xe0/0x3d0
[<
ffffffff815ae90e>] vfs_read+0xde/0x2d0
[<
ffffffff815b1d01>] SyS_read+0x111/0x230
[<
ffffffff822e8636>] entry_SYSCALL_64_fastpath+0x16/0x76
-> #0 (s_active#171){++++.+}:
[<
ffffffff81244f24>] __lock_acquire+0x4304/0x5990
[<
ffffffff8124817d>] lock_acquire+0x13d/0x320
[<
ffffffff81717d3a>] __kernfs_remove+0x58a/0x810
[<
ffffffff8171a115>] kernfs_remove_by_name_ns+0x45/0xa0
[<
ffffffff81721592>] remove_files.isra.0+0x72/0x190
[<
ffffffff8172174b>] sysfs_remove_group+0x9b/0x150
[<
ffffffff81721854>] sysfs_remove_groups+0x54/0xa0
[<
ffffffff81cd97d0>] device_remove_attrs+0xb0/0x140
[<
ffffffff81cdb27c>] device_del+0x38c/0x6b0
[<
ffffffffa0724b8b>] rc_register_device+0x8cb/0x1450 [rc_core]
[<
ffffffffa1326a7b>] dvb_usb_remote_init+0x66b/0x14d0 [dvb_usb]
[<
ffffffffa1321c81>] dvb_usb_device_init+0xf21/0x1860 [dvb_usb]
[<
ffffffffa13517dc>] dib0700_probe+0x14c/0x410 [dvb_usb_dib0700]
[<
ffffffff81dbb1dd>] usb_probe_interface+0x45d/0x940
[<
ffffffff81ce7e7a>] driver_probe_device+0x21a/0xc30
[<
ffffffff81ce89b1>] __driver_attach+0x121/0x160
[<
ffffffff81ce21bf>] bus_for_each_dev+0x11f/0x1a0
[<
ffffffff81ce6cdd>] driver_attach+0x3d/0x50
[<
ffffffff81ce5df9>] bus_add_driver+0x4c9/0x770
[<
ffffffff81cea39c>] driver_register+0x18c/0x3b0
[<
ffffffff81db6e98>] usb_register_driver+0x1f8/0x440
[<
ffffffffa074001e>] dib0700_driver_init+0x1e/0x1000 [dvb_usb_dib0700]
[<
ffffffff810021b1>] do_one_initcall+0x141/0x300
[<
ffffffff8144d8eb>] do_init_module+0x1d0/0x5ad
[<
ffffffff812f27b6>] load_module+0x6666/0x9ba0
[<
ffffffff812f5fe8>] SyS_finit_module+0x108/0x130
[<
ffffffff822e8636>] entry_SYSCALL_64_fastpath+0x16/0x76
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(&dev->lock);
lock(s_active#171);
lock(&dev->lock);
lock(s_active#171);
*** DEADLOCK ***
3 locks held by systemd-udevd/2681:
#0: (&dev->mutex){......}, at: [<
ffffffff81ce8933>] __driver_attach+0xa3/0x160
#1: (&dev->mutex){......}, at: [<
ffffffff81ce8941>] __driver_attach+0xb1/0x160
#2: (&dev->lock){+.+.+.}, at: [<
ffffffffa0724def>] rc_register_device+0xb2f/0x1450 [rc_core]
In this specific case, some error happened during device init,
causing IR to be disabled.
Let's fix it by adding a var that will tell when the device is
initialized. Any calls before that will return a -EINVAL.
That should prevent the race issues.
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
return -EINVAL;
mutex_lock(&rdev->lock);
+ if (!rdev->initialized) {
+ rval = -EINVAL;
+ goto unlock;
+ }
+
if (!rdev->users++ && rdev->open != NULL)
rval = rdev->open(rdev);
if (rval)
rdev->users--;
+unlock:
mutex_unlock(&rdev->lock);
return rval;
return -EINVAL;
mutex_lock(&dev->lock);
+ if (!dev->initialized) {
+ mutex_unlock(&dev->lock);
+ return -EINVAL;
+ }
if (fattr->type == RC_FILTER_NORMAL) {
enabled = dev->enabled_protocols;
}
mutex_lock(&dev->lock);
+ if (!dev->initialized) {
+ rc = -EINVAL;
+ goto out;
+ }
old_protocols = *current_protocols;
new_protocols = old_protocols;
if (!dev)
return -EINVAL;
+ mutex_lock(&dev->lock);
+ if (!dev->initialized) {
+ mutex_unlock(&dev->lock);
+ return -EINVAL;
+ }
+
if (fattr->type == RC_FILTER_NORMAL)
filter = &dev->scancode_filter;
else
filter = &dev->scancode_wakeup_filter;
- mutex_lock(&dev->lock);
if (fattr->mask)
val = filter->mask;
else
return -EINVAL;
mutex_lock(&dev->lock);
+ if (!dev->initialized) {
+ ret = -EINVAL;
+ goto unlock;
+ }
new_filter = *filter;
if (fattr->mask)
dev->sysfs_groups[attr++] = &rc_dev_wakeup_protocol_attr_grp;
dev->sysfs_groups[attr++] = NULL;
- /*
- * Take the lock here, as the device sysfs node will appear
- * when device_add() is called, which may trigger an ir-keytable udev
- * rule, which will in turn call show_protocols and access
- * dev->enabled_protocols before it has been initialized.
- */
- mutex_lock(&dev->lock);
-
rc = device_add(&dev->dev);
if (rc)
goto out_unlock;
dev->input_dev->phys = dev->input_phys;
dev->input_dev->name = dev->input_name;
- /* input_register_device can call ir_open, so unlock mutex here */
- mutex_unlock(&dev->lock);
-
rc = input_register_device(dev->input_dev);
-
- mutex_lock(&dev->lock);
-
if (rc)
goto out_table;
request_module_nowait("ir-lirc-codec");
raw_init = true;
}
- /* calls ir_register_device so unlock mutex here*/
- mutex_unlock(&dev->lock);
rc = ir_raw_event_register(dev);
- mutex_lock(&dev->lock);
if (rc < 0)
goto out_input;
}
dev->enabled_protocols = rc_type;
}
+ mutex_lock(&dev->lock);
+ dev->initialized = true;
mutex_unlock(&dev->lock);
IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n",
out_dev:
device_del(&dev->dev);
out_unlock:
- mutex_unlock(&dev->lock);
ida_simple_remove(&rc_ida, minor);
return rc;
}
/**
* struct rc_dev - represents a remote control device
* @dev: driver model's view of this device
+ * @initialized: true if the device init has completed
* @sysfs_groups: sysfs attribute groups
* @input_name: name of the input child device
* @input_phys: physical path to the input child device
*/
struct rc_dev {
struct device dev;
+ bool initialized;
const struct attribute_group *sysfs_groups[5];
const char *input_name;
const char *input_phys;