Change the rc_register_device() code so that it isn't necessary to hold any mutex. When device_add() is called, the norm is that the device should actually be ready for use. Holding the mutex is a recipe for deadlocks as (for example) calling input_register_device() is quite likely to end up in a call to input_dev->open() which might take the same mutex (to update the user count, see later patches). Signed-off-by: David Härdeman <david@xxxxxxxxxxx> --- drivers/media/rc/rc-main.c | 95 ++++++++++++++++++-------------------------- include/media/rc-core.h | 3 - 2 files changed, 40 insertions(+), 58 deletions(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 83ea507..620cd8d 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -214,8 +214,8 @@ static struct { * It returns the protocol names of supported protocols. * Enabled protocols are printed in brackets. * - * dev->lock is taken to guard against races between device - * registration, store_protocols and show_protocols. + * dev->lock is taken to guard against races between store_protocols + * and show_protocols. */ static ssize_t show_protocols(struct device *device, struct device_attribute *mattr, char *buf) @@ -276,8 +276,8 @@ static ssize_t show_protocols(struct device *device, * Returns -EINVAL if an invalid protocol combination or unknown protocol name * is used, otherwise @len. * - * dev->lock is taken to guard against races between device - * registration, store_protocols and show_protocols. + * dev->lock is taken to guard against races between store_protocols and + * show_protocols. */ static ssize_t store_protocols(struct device *device, struct device_attribute *mattr, @@ -492,17 +492,14 @@ int rc_register_device(struct rc_dev *dev) if (rc) return rc; - for (i = 0; i < ARRAY_SIZE(rc_dev_table); i++) { - if (!rc_dev_table[i]) { - rc_dev_table[i] = dev; + for (i = 0; i < ARRAY_SIZE(rc_dev_table); i++) + if (!rc_dev_table[i]) break; - } - } - - mutex_unlock(&rc_dev_table_mutex); - if (i >= ARRAY_SIZE(rc_dev_table)) - return -ENFILE; + if (i >= ARRAY_SIZE(rc_dev_table)) { + rc = -ENFILE; + goto out; + } dev->minor = i; dev->dev.devt = MKDEV(rc_major, dev->minor); @@ -512,35 +509,9 @@ int rc_register_device(struct rc_dev *dev) if (dev->tx_ir) { rc = kfifo_alloc(&dev->txfifo, RC_TX_KFIFO_SIZE, GFP_KERNEL); if (rc) - goto out_minor; - } - - /* - * 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 either - * dev->rc_map.rc_type or dev->raw->enabled_protocols before it has - * been initialized. - */ - mutex_lock(&dev->lock); - - dev->kt = rc_keytable_create(dev, dev->map_name); - if (!dev->kt) { - rc = -ENOMEM; - goto out_unlock; + goto out; } - rc = device_add(&dev->dev); - if (rc) - goto out_keytable; - - path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); - printk(KERN_INFO "%s: %s as %s\n", - dev_name(&dev->dev), - dev->input_name ? dev->input_name : "Unspecified device", - path ? path : "N/A"); - kfree(path); - if (dev->driver_type == RC_DRIVER_IR_RAW) { /* Load raw decoders, if they aren't already */ if (!raw_init) { @@ -550,7 +521,7 @@ int rc_register_device(struct rc_dev *dev) } rc = ir_raw_event_register(dev); if (rc < 0) - goto out_dev; + goto out_kfifo; } if (dev->change_protocol) { @@ -559,29 +530,41 @@ int rc_register_device(struct rc_dev *dev) goto out_raw; } + rc_dev_table[i] = dev; dev->exist = true; - mutex_unlock(&dev->lock); - IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n", - dev->minor, - dev->driver_name ? dev->driver_name : "unknown", - dev->map_name ? dev->map_name : "unknown", - dev->driver_type == RC_DRIVER_IR_RAW ? "raw" : "cooked"); + /* Once device_add is called, userspace might access e.g. sysfs files */ + rc = device_add(&dev->dev); + if (rc) + goto out_chardev; + + dev->kt = rc_keytable_create(dev, dev->map_name); + if (!dev->kt) { + rc = -ENOMEM; + goto out_device; + } + + mutex_unlock(&rc_dev_table_mutex); + + path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); + printk(KERN_INFO "%s: %s as %s\n", + dev_name(&dev->dev), + dev->input_name ? dev->input_name : "Unspecified device", + path ? path : "N/A"); + kfree(path); return 0; +out_device: + device_del(&dev->dev); +out_chardev: + rc_dev_table[dev->minor] = NULL; out_raw: if (dev->driver_type == RC_DRIVER_IR_RAW) ir_raw_event_unregister(dev); -out_dev: - device_del(&dev->dev); -out_keytable: - rc_keytable_destroy(dev->kt); -out_unlock: - mutex_unlock(&dev->lock); -out_minor: - mutex_lock(&rc_dev_table_mutex); - rc_dev_table[dev->minor] = NULL; +out_kfifo: + kfifo_free(&dev->txfifo); +out: mutex_unlock(&rc_dev_table_mutex); return rc; } diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 20bd1ce..e34815b 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -203,8 +203,7 @@ struct ir_raw_event { * @driver_name: name of the hardware driver which registered this device * @map_name: name of the default keymap * @rc_map: current scan/key table - * @lock: used to ensure we've filled in all protocol details before - * anyone can call show_protocols or store_protocols + * @lock: used where a more specific lock/mutex/etc is not available * @minor: unique minor remote control device number * @exist: used to determine if the device is still valid * @client_list: list of clients (processes which have opened the rc chardev) -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html