diff options
author | Jean Delvare <khali@linux-fr.org> | 2009-11-26 03:22:33 -0500 |
---|---|---|
committer | Jean Delvare <khali@linux-fr.org> | 2009-11-26 03:22:33 -0500 |
commit | bbd2d9c9198c6efd449e9d395b3eaf2d03aa3bba (patch) | |
tree | 671d1810b5adaf49d2166ef238dc247ad05f0d00 /drivers/i2c/i2c-core.c | |
parent | 03b70d625c10d1605012d41489d9df18467c5f55 (diff) |
i2c: Fix userspace_device list corruption
Fix userspace_device list corruption. The corruption was caused by
clients not being removed when adapters with such clients were
themselves removed. Something like the following would trigger it
(assuming i2c-stub gets adapter number 3):
# modprobe i2c-stub chip_addr=0x50
# echo 24c08 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
# rmmod i2c-stub
# modprobe i2c-stub chip_addr=0x50
# echo 24c08 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
For the records, the stack trace in the kernel logs look like this:
kernel: WARNING: at lib/list_debug.c:30 __list_add+0x8b/0x90()
kernel: Hardware name: (...)
kernel: list_add corruption. prev->next should be next (c137fc84), but was (null). (prev=f57111b8).
kernel: Modules linked in: (...)
kernel: Pid: 4669, comm: bash Not tainted 2.6.32-rc8 #259
kernel: Call Trace:
kernel: [<c111eb8b>] ? __list_add+0x8b/0x90
kernel: [<c111eb8b>] ? __list_add+0x8b/0x90
kernel: [<c103265c>] warn_slowpath_common+0x6c/0xc0
kernel: [<c111eb8b>] ? __list_add+0x8b/0x90
kernel: [<c10326f6>] warn_slowpath_fmt+0x26/0x30
kernel: [<c111eb8b>] __list_add+0x8b/0x90
kernel: [<c11ba165>] i2c_sysfs_new_device+0x1c5/0x250
kernel: [<c10861be>] ? might_fault+0x2e/0x80
kernel: [<c11b9fa0>] ? i2c_sysfs_new_device+0x0/0x250
kernel: [<c118c625>] dev_attr_store+0x25/0x30
kernel: [<c10e305c>] sysfs_write_file+0x9c/0xf0
kernel: [<c109d35c>] vfs_write+0x9c/0x160
kernel: [<c10e2fc0>] ? sysfs_write_file+0x0/0xf0
kernel: [<c109d4dd>] sys_write+0x3d/0x70
kernel: [<c1002ed8>] sysenter_do_call+0x12/0x36
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c/i2c-core.c')
-rw-r--r-- | drivers/i2c/i2c-core.c | 11 |
1 files changed, 11 insertions, 0 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 8d80fceca6a..29650435514 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c | |||
@@ -762,6 +762,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) | |||
762 | { | 762 | { |
763 | int res = 0; | 763 | int res = 0; |
764 | struct i2c_adapter *found; | 764 | struct i2c_adapter *found; |
765 | struct i2c_client *client, *next; | ||
765 | 766 | ||
766 | /* First make sure that this adapter was ever added */ | 767 | /* First make sure that this adapter was ever added */ |
767 | mutex_lock(&core_lock); | 768 | mutex_lock(&core_lock); |
@@ -781,6 +782,16 @@ int i2c_del_adapter(struct i2c_adapter *adap) | |||
781 | if (res) | 782 | if (res) |
782 | return res; | 783 | return res; |
783 | 784 | ||
785 | /* Remove devices instantiated from sysfs */ | ||
786 | list_for_each_entry_safe(client, next, &userspace_devices, detected) { | ||
787 | if (client->adapter == adap) { | ||
788 | dev_dbg(&adap->dev, "Removing %s at 0x%x\n", | ||
789 | client->name, client->addr); | ||
790 | list_del(&client->detected); | ||
791 | i2c_unregister_device(client); | ||
792 | } | ||
793 | } | ||
794 | |||
784 | /* Detach any active clients. This can't fail, thus we do not | 795 | /* Detach any active clients. This can't fail, thus we do not |
785 | checking the returned value. */ | 796 | checking the returned value. */ |
786 | res = device_for_each_child(&adap->dev, NULL, __unregister_client); | 797 | res = device_for_each_child(&adap->dev, NULL, __unregister_client); |