diff options
author | Jean Delvare <khali@linux-fr.org> | 2010-05-04 05:09:28 -0400 |
---|---|---|
committer | Jean Delvare <khali@linux-fr.org> | 2010-05-04 05:09:28 -0400 |
commit | 6629dcff19470a894ce294d0adb9cbab94ee1fb9 (patch) | |
tree | 18f75a3f5ca83de96a0fe353fafa02bab174cd36 /drivers/i2c | |
parent | b1d4b390ea4bb480e65974ce522a04022608a8df (diff) |
i2c-core: Use per-adapter userspace device lists
Using a single list for all userspace devices leads to a dead lock
on multiplexed buses in some circumstances (mux chip instantiated
from userspace). This is solved by using a separate list for each
bus segment.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Acked-by: Michael Lawnick <ml.lawnick@gmx.de>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/i2c-core.c | 34 |
1 files changed, 18 insertions, 16 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 5105126225c3..c2258a51fe0c 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c | |||
@@ -40,12 +40,11 @@ | |||
40 | #include "i2c-core.h" | 40 | #include "i2c-core.h" |
41 | 41 | ||
42 | 42 | ||
43 | /* core_lock protects i2c_adapter_idr, userspace_devices, and guarantees | 43 | /* core_lock protects i2c_adapter_idr, and guarantees |
44 | that device detection, deletion of detected devices, and attach_adapter | 44 | that device detection, deletion of detected devices, and attach_adapter |
45 | and detach_adapter calls are serialized */ | 45 | and detach_adapter calls are serialized */ |
46 | static DEFINE_MUTEX(core_lock); | 46 | static DEFINE_MUTEX(core_lock); |
47 | static DEFINE_IDR(i2c_adapter_idr); | 47 | static DEFINE_IDR(i2c_adapter_idr); |
48 | static LIST_HEAD(userspace_devices); | ||
49 | 48 | ||
50 | static struct device_type i2c_client_type; | 49 | static struct device_type i2c_client_type; |
51 | static int i2c_check_addr(struct i2c_adapter *adapter, int addr); | 50 | static int i2c_check_addr(struct i2c_adapter *adapter, int addr); |
@@ -542,9 +541,9 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr, | |||
542 | return -EEXIST; | 541 | return -EEXIST; |
543 | 542 | ||
544 | /* Keep track of the added device */ | 543 | /* Keep track of the added device */ |
545 | mutex_lock(&core_lock); | 544 | i2c_lock_adapter(adap); |
546 | list_add_tail(&client->detected, &userspace_devices); | 545 | list_add_tail(&client->detected, &adap->userspace_clients); |
547 | mutex_unlock(&core_lock); | 546 | i2c_unlock_adapter(adap); |
548 | dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", | 547 | dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", |
549 | info.type, info.addr); | 548 | info.type, info.addr); |
550 | 549 | ||
@@ -583,9 +582,10 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, | |||
583 | 582 | ||
584 | /* Make sure the device was added through sysfs */ | 583 | /* Make sure the device was added through sysfs */ |
585 | res = -ENOENT; | 584 | res = -ENOENT; |
586 | mutex_lock(&core_lock); | 585 | i2c_lock_adapter(adap); |
587 | list_for_each_entry_safe(client, next, &userspace_devices, detected) { | 586 | list_for_each_entry_safe(client, next, &adap->userspace_clients, |
588 | if (client->addr == addr && client->adapter == adap) { | 587 | detected) { |
588 | if (client->addr == addr) { | ||
589 | dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", | 589 | dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", |
590 | "delete_device", client->name, client->addr); | 590 | "delete_device", client->name, client->addr); |
591 | 591 | ||
@@ -595,7 +595,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, | |||
595 | break; | 595 | break; |
596 | } | 596 | } |
597 | } | 597 | } |
598 | mutex_unlock(&core_lock); | 598 | i2c_unlock_adapter(adap); |
599 | 599 | ||
600 | if (res < 0) | 600 | if (res < 0) |
601 | dev_err(dev, "%s: Can't find device in list\n", | 601 | dev_err(dev, "%s: Can't find device in list\n", |
@@ -677,6 +677,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) | |||
677 | } | 677 | } |
678 | 678 | ||
679 | rt_mutex_init(&adap->bus_lock); | 679 | rt_mutex_init(&adap->bus_lock); |
680 | INIT_LIST_HEAD(&adap->userspace_clients); | ||
680 | 681 | ||
681 | /* Set default timeout to 1 second if not already set */ | 682 | /* Set default timeout to 1 second if not already set */ |
682 | if (adap->timeout == 0) | 683 | if (adap->timeout == 0) |
@@ -879,14 +880,15 @@ int i2c_del_adapter(struct i2c_adapter *adap) | |||
879 | return res; | 880 | return res; |
880 | 881 | ||
881 | /* Remove devices instantiated from sysfs */ | 882 | /* Remove devices instantiated from sysfs */ |
882 | list_for_each_entry_safe(client, next, &userspace_devices, detected) { | 883 | i2c_lock_adapter(adap); |
883 | if (client->adapter == adap) { | 884 | list_for_each_entry_safe(client, next, &adap->userspace_clients, |
884 | dev_dbg(&adap->dev, "Removing %s at 0x%x\n", | 885 | detected) { |
885 | client->name, client->addr); | 886 | dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, |
886 | list_del(&client->detected); | 887 | client->addr); |
887 | i2c_unregister_device(client); | 888 | list_del(&client->detected); |
888 | } | 889 | i2c_unregister_device(client); |
889 | } | 890 | } |
891 | i2c_unlock_adapter(adap); | ||
890 | 892 | ||
891 | /* Detach any active clients. This can't fail, thus we do not | 893 | /* Detach any active clients. This can't fail, thus we do not |
892 | checking the returned value. */ | 894 | checking the returned value. */ |